mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-25 05:27:42 +00:00
Detect real age of call request by using server timestamps.
This commit is contained in:
committed by
Alan Evans
parent
891a1af995
commit
629ba105cb
@@ -16,6 +16,7 @@ import org.signal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
|
||||
import org.signal.zkgroup.profiles.ProfileKeyVersion;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.libsignal.logging.Log;
|
||||
import org.whispersystems.libsignal.util.Hex;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
@@ -61,6 +62,8 @@ public class SignalServiceMessagePipe {
|
||||
|
||||
private static final String TAG = SignalServiceMessagePipe.class.getName();
|
||||
|
||||
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||
|
||||
private final WebSocketConnection websocket;
|
||||
private final Optional<CredentialsProvider> credentialsProvider;
|
||||
private final ClientZkProfileOperations clientZkProfile;
|
||||
@@ -146,9 +149,21 @@ public class SignalServiceMessagePipe {
|
||||
|
||||
try {
|
||||
if (isSignalServiceEnvelope(request)) {
|
||||
Optional<String> timestampHeader = findHeader(request, SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||
long timestamp = 0;
|
||||
|
||||
if (timestampHeader.isPresent()) {
|
||||
try {
|
||||
timestamp = Long.parseLong(timestampHeader.get());
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "Failed to parse " + SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||
}
|
||||
}
|
||||
|
||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(),
|
||||
credentialsProvider.get().getSignalingKey(),
|
||||
signalKeyEncrypted);
|
||||
signalKeyEncrypted,
|
||||
timestamp);
|
||||
|
||||
callback.onMessage(envelope);
|
||||
return Optional.of(envelope);
|
||||
@@ -345,6 +360,23 @@ public class SignalServiceMessagePipe {
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<String> findHeader(WebSocketRequestMessage message, String targetHeader) {
|
||||
if (message.getHeadersCount() == 0) {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
for (String header : message.getHeadersList()) {
|
||||
if (header.startsWith(targetHeader)) {
|
||||
String[] split = header.split(":");
|
||||
if (split.length == 2 && split[0].trim().toLowerCase().equals(targetHeader.toLowerCase())) {
|
||||
return Optional.of(split[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
/**
|
||||
* For receiving a callback when a new message has been
|
||||
* received.
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceEnvelopeEntity;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceMessagesResult;
|
||||
import org.whispersystems.signalservice.internal.sticker.StickerProtos;
|
||||
import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider;
|
||||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
@@ -263,22 +264,31 @@ public class SignalServiceMessageReceiver {
|
||||
public List<SignalServiceEnvelope> retrieveMessages(MessageReceivedCallback callback)
|
||||
throws IOException
|
||||
{
|
||||
List<SignalServiceEnvelope> results = new LinkedList<>();
|
||||
List<SignalServiceEnvelopeEntity> entities = socket.getMessages();
|
||||
List<SignalServiceEnvelope> results = new LinkedList<>();
|
||||
SignalServiceMessagesResult messageResult = socket.getMessages();
|
||||
|
||||
for (SignalServiceEnvelopeEntity entity : entities) {
|
||||
for (SignalServiceEnvelopeEntity entity : messageResult.getEnvelopes()) {
|
||||
SignalServiceEnvelope envelope;
|
||||
|
||||
if (entity.hasSource() && entity.getSourceDevice() > 0) {
|
||||
SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(entity.getSourceUuid()), entity.getSourceE164());
|
||||
envelope = new SignalServiceEnvelope(entity.getType(), Optional.of(address),
|
||||
entity.getSourceDevice(), entity.getTimestamp(),
|
||||
entity.getMessage(), entity.getContent(),
|
||||
entity.getServerTimestamp(), entity.getServerUuid());
|
||||
envelope = new SignalServiceEnvelope(entity.getType(),
|
||||
Optional.of(address),
|
||||
entity.getSourceDevice(),
|
||||
entity.getTimestamp(),
|
||||
entity.getMessage(),
|
||||
entity.getContent(),
|
||||
entity.getServerTimestamp(),
|
||||
messageResult.getServerDeliveredTimestamp(),
|
||||
entity.getServerUuid());
|
||||
} else {
|
||||
envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(),
|
||||
entity.getMessage(), entity.getContent(),
|
||||
entity.getServerTimestamp(), entity.getServerUuid());
|
||||
envelope = new SignalServiceEnvelope(entity.getType(),
|
||||
entity.getTimestamp(),
|
||||
entity.getMessage(),
|
||||
entity.getContent(),
|
||||
entity.getServerTimestamp(),
|
||||
messageResult.getServerDeliveredTimestamp(),
|
||||
entity.getServerUuid());
|
||||
}
|
||||
|
||||
callback.onMessage(envelope);
|
||||
|
||||
@@ -176,23 +176,23 @@ public class SignalServiceCipher {
|
||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false);
|
||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||
sessionVersion = sessionCipher.getSessionVersion();
|
||||
} else if (envelope.isSignalMessage()) {
|
||||
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false);
|
||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||
sessionVersion = sessionCipher.getSessionVersion();
|
||||
} else if (envelope.isUnidentifiedSender()) {
|
||||
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1);
|
||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerTimestamp());
|
||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerReceivedTimestamp());
|
||||
SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.parse(result.getSenderUuid().orNull()), result.getSenderE164());
|
||||
SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId());
|
||||
|
||||
paddedMessage = result.getPaddedMessage();
|
||||
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), envelope.getServerTimestamp(), true);
|
||||
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), true);
|
||||
sessionVersion = sealedSessionCipher.getSessionVersion(protocolAddress);
|
||||
} else {
|
||||
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
||||
|
||||
@@ -59,7 +59,8 @@ public final class SignalServiceContent {
|
||||
private final SignalServiceAddress sender;
|
||||
private final int senderDevice;
|
||||
private final long timestamp;
|
||||
private final long serverTimestamp;
|
||||
private final long serverReceivedTimestamp;
|
||||
private final long serverDeliveredTimestamp;
|
||||
private final boolean needsReceipt;
|
||||
private final SignalServiceContentProto serializedState;
|
||||
|
||||
@@ -69,13 +70,22 @@ public final class SignalServiceContent {
|
||||
private final Optional<SignalServiceReceiptMessage> readMessage;
|
||||
private final Optional<SignalServiceTypingMessage> typingMessage;
|
||||
|
||||
private SignalServiceContent(SignalServiceDataMessage message, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
private SignalServiceContent(SignalServiceDataMessage message,
|
||||
SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt,
|
||||
SignalServiceContentProto serializedState)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
|
||||
this.message = Optional.fromNullable(message);
|
||||
this.synchronizeMessage = Optional.absent();
|
||||
@@ -84,13 +94,22 @@ public final class SignalServiceContent {
|
||||
this.typingMessage = Optional.absent();
|
||||
}
|
||||
|
||||
private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage,
|
||||
SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt,
|
||||
SignalServiceContentProto serializedState)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
|
||||
this.message = Optional.absent();
|
||||
this.synchronizeMessage = Optional.fromNullable(synchronizeMessage);
|
||||
@@ -99,13 +118,22 @@ public final class SignalServiceContent {
|
||||
this.typingMessage = Optional.absent();
|
||||
}
|
||||
|
||||
private SignalServiceContent(SignalServiceCallMessage callMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
private SignalServiceContent(SignalServiceCallMessage callMessage,
|
||||
SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt,
|
||||
SignalServiceContentProto serializedState)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
|
||||
this.message = Optional.absent();
|
||||
this.synchronizeMessage = Optional.absent();
|
||||
@@ -114,13 +142,22 @@ public final class SignalServiceContent {
|
||||
this.typingMessage = Optional.absent();
|
||||
}
|
||||
|
||||
private SignalServiceContent(SignalServiceReceiptMessage receiptMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
private SignalServiceContent(SignalServiceReceiptMessage receiptMessage,
|
||||
SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt,
|
||||
SignalServiceContentProto serializedState)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
|
||||
this.message = Optional.absent();
|
||||
this.synchronizeMessage = Optional.absent();
|
||||
@@ -129,13 +166,22 @@ public final class SignalServiceContent {
|
||||
this.typingMessage = Optional.absent();
|
||||
}
|
||||
|
||||
private SignalServiceContent(SignalServiceTypingMessage typingMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
private SignalServiceContent(SignalServiceTypingMessage typingMessage,
|
||||
SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt,
|
||||
SignalServiceContentProto serializedState)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
this.serializedState = serializedState;
|
||||
|
||||
this.message = Optional.absent();
|
||||
this.synchronizeMessage = Optional.absent();
|
||||
@@ -176,8 +222,12 @@ public final class SignalServiceContent {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public long getServerTimestamp() {
|
||||
return serverTimestamp;
|
||||
public long getServerReceivedTimestamp() {
|
||||
return serverReceivedTimestamp;
|
||||
}
|
||||
|
||||
public long getServerDeliveredTimestamp() {
|
||||
return serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public boolean isNeedsReceipt() {
|
||||
@@ -217,7 +267,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.isNeedsReceipt(),
|
||||
serviceContentProto);
|
||||
} else if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.CONTENT) {
|
||||
@@ -228,7 +279,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.isNeedsReceipt(),
|
||||
serviceContentProto);
|
||||
} else if (message.hasSyncMessage() && localAddress.matches(metadata.getSender())) {
|
||||
@@ -236,7 +288,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.isNeedsReceipt(),
|
||||
serviceContentProto);
|
||||
} else if (message.hasCallMessage()) {
|
||||
@@ -244,7 +297,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.isNeedsReceipt(),
|
||||
serviceContentProto);
|
||||
} else if (message.hasReceiptMessage()) {
|
||||
@@ -252,7 +306,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.isNeedsReceipt(),
|
||||
serviceContentProto);
|
||||
} else if (message.hasTypingMessage()) {
|
||||
@@ -260,7 +315,8 @@ public final class SignalServiceContent {
|
||||
metadata.getSender(),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
false,
|
||||
serviceContentProto);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public class SignalServiceEnvelope {
|
||||
private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH;
|
||||
|
||||
private final Envelope envelope;
|
||||
private final long serverDeliveredTimestamp;
|
||||
|
||||
/**
|
||||
* Construct an envelope from a serialized, Base64 encoded SignalServiceEnvelope, encrypted
|
||||
@@ -65,10 +66,13 @@ public class SignalServiceEnvelope {
|
||||
* @throws IOException
|
||||
* @throws InvalidVersionException
|
||||
*/
|
||||
public SignalServiceEnvelope(String message, String signalingKey, boolean isSignalingKeyEncrypted)
|
||||
public SignalServiceEnvelope(String message,
|
||||
String signalingKey,
|
||||
boolean isSignalingKeyEncrypted,
|
||||
long serverDeliveredTimestamp)
|
||||
throws IOException, InvalidVersionException
|
||||
{
|
||||
this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted);
|
||||
this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted, serverDeliveredTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +83,10 @@ public class SignalServiceEnvelope {
|
||||
* @throws InvalidVersionException
|
||||
* @throws IOException
|
||||
*/
|
||||
public SignalServiceEnvelope(byte[] input, String signalingKey, boolean isSignalingKeyEncrypted)
|
||||
public SignalServiceEnvelope(byte[] input,
|
||||
String signalingKey,
|
||||
boolean isSignalingKeyEncrypted,
|
||||
long serverDeliveredTimestamp)
|
||||
throws InvalidVersionException, IOException
|
||||
{
|
||||
if (!isSignalingKeyEncrypted) {
|
||||
@@ -96,14 +103,25 @@ public class SignalServiceEnvelope {
|
||||
|
||||
this.envelope = Envelope.parseFrom(getPlaintext(input, cipherKey));
|
||||
}
|
||||
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public SignalServiceEnvelope(int type, Optional<SignalServiceAddress> sender, int senderDevice, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
||||
public SignalServiceEnvelope(int type,
|
||||
Optional<SignalServiceAddress> sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
byte[] legacyMessage,
|
||||
byte[] content,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
String uuid)
|
||||
{
|
||||
Envelope.Builder builder = Envelope.newBuilder()
|
||||
.setType(Envelope.Type.valueOf(type))
|
||||
.setSourceDevice(senderDevice)
|
||||
.setTimestamp(timestamp)
|
||||
.setServerTimestamp(serverTimestamp);
|
||||
.setServerTimestamp(serverReceivedTimestamp);
|
||||
|
||||
if (sender.isPresent()) {
|
||||
if (sender.get().getUuid().isPresent()) {
|
||||
@@ -122,14 +140,22 @@ public class SignalServiceEnvelope {
|
||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||
|
||||
this.envelope = builder.build();
|
||||
this.envelope = builder.build();
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public SignalServiceEnvelope(int type, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
||||
public SignalServiceEnvelope(int type,
|
||||
long timestamp,
|
||||
byte[] legacyMessage,
|
||||
byte[] content,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
String uuid)
|
||||
{
|
||||
Envelope.Builder builder = Envelope.newBuilder()
|
||||
.setType(Envelope.Type.valueOf(type))
|
||||
.setTimestamp(timestamp)
|
||||
.setServerTimestamp(serverTimestamp);
|
||||
.setServerTimestamp(serverReceivedTimestamp);
|
||||
|
||||
if (uuid != null) {
|
||||
builder.setServerGuid(uuid);
|
||||
@@ -138,7 +164,8 @@ public class SignalServiceEnvelope {
|
||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||
|
||||
this.envelope = builder.build();
|
||||
this.envelope = builder.build();
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
@@ -206,10 +233,20 @@ public class SignalServiceEnvelope {
|
||||
return envelope.getTimestamp();
|
||||
}
|
||||
|
||||
public long getServerTimestamp() {
|
||||
/**
|
||||
* @return The server timestamp of when the server received the envelope.
|
||||
*/
|
||||
public long getServerReceivedTimestamp() {
|
||||
return envelope.getServerTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The server timestamp of when the envelope was delivered to us.
|
||||
*/
|
||||
public long getServerDeliveredTimestamp() {
|
||||
return serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the envelope contains a SignalServiceDataMessage
|
||||
*/
|
||||
|
||||
@@ -6,15 +6,23 @@ public final class SignalServiceMetadata {
|
||||
private final SignalServiceAddress sender;
|
||||
private final int senderDevice;
|
||||
private final long timestamp;
|
||||
private final long serverTimestamp;
|
||||
private final long serverReceivedTimestamp;
|
||||
private final long serverDeliveredTimestamp;
|
||||
private final boolean needsReceipt;
|
||||
|
||||
public SignalServiceMetadata(SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt) {
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverTimestamp = serverTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
public SignalServiceMetadata(SignalServiceAddress sender,
|
||||
int senderDevice,
|
||||
long timestamp,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp,
|
||||
boolean needsReceipt)
|
||||
{
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
this.timestamp = timestamp;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
this.needsReceipt = needsReceipt;
|
||||
}
|
||||
|
||||
public SignalServiceAddress getSender() {
|
||||
@@ -29,8 +37,12 @@ public final class SignalServiceMetadata {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public long getServerTimestamp() {
|
||||
return serverTimestamp;
|
||||
public long getServerReceivedTimestamp() {
|
||||
return serverReceivedTimestamp;
|
||||
}
|
||||
|
||||
public long getServerDeliveredTimestamp() {
|
||||
return serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public boolean isNeedsReceipt() {
|
||||
|
||||
@@ -198,6 +198,8 @@ public class PushServiceSocket {
|
||||
private static final String GROUPSV2_GROUP_CHANGES = "/v1/groups/logs/%s";
|
||||
private static final String GROUPSV2_AVATAR_REQUEST = "/v1/groups/avatar/form";
|
||||
|
||||
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
|
||||
|
||||
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
|
||||
private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler();
|
||||
|
||||
@@ -390,9 +392,28 @@ public class PushServiceSocket {
|
||||
});
|
||||
}
|
||||
|
||||
public List<SignalServiceEnvelopeEntity> getMessages() throws IOException {
|
||||
String responseText = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", null);
|
||||
return JsonUtil.fromJson(responseText, SignalServiceEnvelopeEntityList.class).getMessages();
|
||||
public SignalServiceMessagesResult getMessages() throws IOException {
|
||||
Response response = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", (RequestBody) null, NO_HEADERS, NO_HANDLER, Optional.absent());
|
||||
validateServiceResponse(response);
|
||||
|
||||
List<SignalServiceEnvelopeEntity> envelopes;
|
||||
try {
|
||||
envelopes = JsonUtil.fromJson(readBodyString(response.body()), SignalServiceEnvelopeEntityList.class).getMessages();
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
}
|
||||
|
||||
long serverDeliveredTimestamp = 0;
|
||||
try {
|
||||
String stringValue = response.header(SERVER_DELIVERED_TIMESTAMP_HEADER);
|
||||
stringValue = stringValue != null ? stringValue : "0";
|
||||
|
||||
serverDeliveredTimestamp = Long.parseLong(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return new SignalServiceMessagesResult(envelopes, serverDeliveredTimestamp);
|
||||
}
|
||||
|
||||
public void acknowledgeMessage(String sender, long timestamp) throws IOException {
|
||||
@@ -1368,9 +1389,9 @@ public class PushServiceSocket {
|
||||
call.enqueue(new Callback() {
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody body = validateServiceResponse(response)) {
|
||||
try (ResponseBody body = validateServiceResponse(response).body()) {
|
||||
try {
|
||||
bodyFuture.set(body.string());
|
||||
bodyFuture.set(readBodyString(body));
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
}
|
||||
@@ -1395,6 +1416,17 @@ public class PushServiceSocket {
|
||||
ResponseCodeHandler responseCodeHandler,
|
||||
Optional<UnidentifiedAccess> unidentifiedAccessKey)
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||
{
|
||||
return makeServiceRequest(urlFragment, method, body, headers, responseCodeHandler, unidentifiedAccessKey).body();
|
||||
}
|
||||
|
||||
private Response makeServiceRequest(String urlFragment,
|
||||
String method,
|
||||
RequestBody body,
|
||||
Map<String, String> headers,
|
||||
ResponseCodeHandler responseCodeHandler,
|
||||
Optional<UnidentifiedAccess> unidentifiedAccessKey)
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||
{
|
||||
Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey);
|
||||
|
||||
@@ -1403,7 +1435,7 @@ public class PushServiceSocket {
|
||||
return validateServiceResponse(response);
|
||||
}
|
||||
|
||||
private ResponseBody validateServiceResponse(Response response) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||
private Response validateServiceResponse(Response response) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||
int responseCode = response.code();
|
||||
String responseMessage = response.message();
|
||||
ResponseBody responseBody = response.body();
|
||||
@@ -1420,7 +1452,7 @@ public class PushServiceSocket {
|
||||
MismatchedDevices mismatchedDevices;
|
||||
|
||||
try {
|
||||
mismatchedDevices = JsonUtil.fromJson(responseBody.string(), MismatchedDevices.class);
|
||||
mismatchedDevices = JsonUtil.fromJson(readBodyString(responseBody), MismatchedDevices.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||
@@ -1433,7 +1465,7 @@ public class PushServiceSocket {
|
||||
StaleDevices staleDevices;
|
||||
|
||||
try {
|
||||
staleDevices = JsonUtil.fromJson(responseBody.string(), StaleDevices.class);
|
||||
staleDevices = JsonUtil.fromJson(readBodyString(responseBody), StaleDevices.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||
} catch (IOException e) {
|
||||
@@ -1445,7 +1477,7 @@ public class PushServiceSocket {
|
||||
DeviceLimit deviceLimit;
|
||||
|
||||
try {
|
||||
deviceLimit = JsonUtil.fromJson(responseBody.string(), DeviceLimit.class);
|
||||
deviceLimit = JsonUtil.fromJson(readBodyString(responseBody), DeviceLimit.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||
} catch (IOException e) {
|
||||
@@ -1459,7 +1491,7 @@ public class PushServiceSocket {
|
||||
RegistrationLockFailure accountLockFailure;
|
||||
|
||||
try {
|
||||
accountLockFailure = JsonUtil.fromJson(responseBody.string(), RegistrationLockFailure.class);
|
||||
accountLockFailure = JsonUtil.fromJson(readBodyString(responseBody), RegistrationLockFailure.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||
@@ -1479,7 +1511,7 @@ public class PushServiceSocket {
|
||||
throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage);
|
||||
}
|
||||
|
||||
return responseBody;
|
||||
return response;
|
||||
}
|
||||
|
||||
private Response getServiceConnection(String urlFragment, String method, RequestBody body, Map<String, String> headers, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||
@@ -1807,6 +1839,15 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
private static String readBodyString(ResponseBody body) throws IOException {
|
||||
if (body != null) {
|
||||
return body.string();
|
||||
} else {
|
||||
throw new IOException("No body!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class GcmRegistrationId {
|
||||
|
||||
@JsonProperty
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class SignalServiceMessagesResult {
|
||||
private final List<SignalServiceEnvelopeEntity> envelopes;
|
||||
private final long serverDeliveredTimestamp;
|
||||
|
||||
SignalServiceMessagesResult(List<SignalServiceEnvelopeEntity> envelopes, long serverDeliveredTimestamp) {
|
||||
this.envelopes = envelopes;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
public List<SignalServiceEnvelopeEntity> getEnvelopes() {
|
||||
return envelopes;
|
||||
}
|
||||
|
||||
public long getServerDeliveredTimestamp() {
|
||||
return serverDeliveredTimestamp;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@ public final class SignalServiceMetadataProtobufSerializer {
|
||||
.setSenderDevice(metadata.getSenderDevice())
|
||||
.setNeedsReceipt(metadata.isNeedsReceipt())
|
||||
.setTimestamp(metadata.getTimestamp())
|
||||
.setServerTimestamp(metadata.getServerTimestamp())
|
||||
.setServerReceivedTimestamp(metadata.getServerReceivedTimestamp())
|
||||
.setServerDeliveredTimestamp(metadata.getServerDeliveredTimestamp())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -22,7 +23,8 @@ public final class SignalServiceMetadataProtobufSerializer {
|
||||
return new SignalServiceMetadata(SignalServiceAddressProtobufSerializer.fromProtobuf(metadata.getAddress()),
|
||||
metadata.getSenderDevice(),
|
||||
metadata.getTimestamp(),
|
||||
metadata.getServerTimestamp(),
|
||||
metadata.getServerReceivedTimestamp(),
|
||||
metadata.getServerDeliveredTimestamp(),
|
||||
metadata.getNeedsReceipt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ message SignalServiceContentProto {
|
||||
}
|
||||
|
||||
message MetadataProto {
|
||||
optional AddressProto address = 1;
|
||||
optional int32 senderDevice = 2;
|
||||
optional int64 timestamp = 3;
|
||||
optional int64 serverTimestamp = 5;
|
||||
optional bool needsReceipt = 4;
|
||||
optional AddressProto address = 1;
|
||||
optional int32 senderDevice = 2;
|
||||
optional int64 timestamp = 3;
|
||||
optional int64 serverReceivedTimestamp = 5;
|
||||
optional int64 serverDeliveredTimestamp = 6;
|
||||
optional bool needsReceipt = 4;
|
||||
}
|
||||
|
||||
message AddressProto {
|
||||
|
||||
Reference in New Issue
Block a user