Increased support for sync message contexts.

1) Surface received sync message contexts in TextSecureMessage
   objects.

2) Send a sync message context for group messages.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-05-18 12:07:02 -07:00
parent a8e1160200
commit ecec3d27f9
5 changed files with 77 additions and 14 deletions

View File

@ -42,6 +42,7 @@ import org.whispersystems.textsecure.internal.push.MismatchedDevices;
import org.whispersystems.textsecure.internal.push.OutgoingPushMessage;
import org.whispersystems.textsecure.internal.push.OutgoingPushMessageList;
import org.whispersystems.textsecure.internal.push.PushAttachmentData;
import org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext;
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
import org.whispersystems.textsecure.internal.push.SendMessageResponse;
import org.whispersystems.textsecure.internal.push.StaleDevices;
@ -121,7 +122,7 @@ public class TextSecureMessageSender {
SendMessageResponse response = sendMessage(recipient, timestamp, content);
if (response != null && response.getNeedsSync()) {
byte[] syncMessage = createSyncMessageContent(content, recipient, timestamp);
byte[] syncMessage = createSyncMessageContent(content, Optional.of(recipient), timestamp);
sendMessage(syncAddress, timestamp, syncMessage);
}
@ -145,8 +146,18 @@ public class TextSecureMessageSender {
public void sendMessage(List<TextSecureAddress> recipients, TextSecureMessage message)
throws IOException, EncapsulatedExceptions
{
byte[] content = createMessageContent(message);
sendMessage(recipients, message.getTimestamp(), content);
byte[] content = createMessageContent(message);
long timestamp = message.getTimestamp();
SendMessageResponse response = sendMessage(recipients, timestamp, content);
try {
if (response != null && response.getNeedsSync()) {
byte[] syncMessage = createSyncMessageContent(content, Optional.<TextSecureAddress>absent(), timestamp);
sendMessage(syncAddress, timestamp, syncMessage);
}
} catch (UntrustedIdentityException e) {
throw new EncapsulatedExceptions(e);
}
}
private byte[] createMessageContent(TextSecureMessage message) throws IOException {
@ -172,13 +183,17 @@ public class TextSecureMessageSender {
return builder.build().toByteArray();
}
private byte[] createSyncMessageContent(byte[] content, TextSecureAddress recipient, long timestamp) {
private byte[] createSyncMessageContent(byte[] content, Optional<TextSecureAddress> recipient, long timestamp) {
try {
SyncMessageContext.Builder syncMessageContext = SyncMessageContext.newBuilder();
syncMessageContext.setTimestamp(timestamp);
if (recipient.isPresent()) {
syncMessageContext.setDestination(recipient.get().getNumber());
}
PushMessageContent.Builder builder = PushMessageContent.parseFrom(content).toBuilder();
builder.setSync(PushMessageContent.SyncMessageContext.newBuilder()
.setDestination(recipient.getNumber())
.setTimestamp(timestamp)
.build());
builder.setSync(syncMessageContext.build());
return builder.build().toByteArray();
} catch (InvalidProtocolBufferException e) {
@ -209,16 +224,18 @@ public class TextSecureMessageSender {
return builder.build();
}
private void sendMessage(List<TextSecureAddress> recipients, long timestamp, byte[] content)
private SendMessageResponse sendMessage(List<TextSecureAddress> recipients, long timestamp, byte[] content)
throws IOException, EncapsulatedExceptions
{
List<UntrustedIdentityException> untrustedIdentities = new LinkedList<>();
List<UnregisteredUserException> unregisteredUsers = new LinkedList<>();
List<NetworkFailureException> networkExceptions = new LinkedList<>();
SendMessageResponse response = null;
for (TextSecureAddress recipient : recipients) {
try {
sendMessage(recipient, timestamp, content);
response = sendMessage(recipient, timestamp, content);
} catch (UntrustedIdentityException e) {
Log.w(TAG, e);
untrustedIdentities.add(e);
@ -234,6 +251,8 @@ public class TextSecureMessageSender {
if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) {
throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions);
}
return response;
}
private SendMessageResponse sendMessage(TextSecureAddress recipient, long timestamp, byte[] content)

View File

@ -37,6 +37,7 @@ import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.api.messages.TextSecureMessage;
import org.whispersystems.textsecure.api.messages.TextSecureSyncContext;
import org.whispersystems.textsecure.internal.push.OutgoingPushMessage;
import org.whispersystems.textsecure.internal.push.PushMessageProtos;
import org.whispersystems.textsecure.internal.push.PushTransportDetails;
@ -126,6 +127,7 @@ public class TextSecureCipher {
private TextSecureMessage createTextSecureMessage(TextSecureEnvelope envelope, PushMessageContent content) {
TextSecureGroup groupInfo = createGroupInfo(envelope, content);
TextSecureSyncContext syncContext = createSyncContext(content);
List<TextSecureAttachment> attachments = new LinkedList<>();
boolean endSession = ((content.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0);
boolean secure = envelope.isWhisperMessage() || envelope.isPreKeyWhisperMessage();
@ -138,7 +140,14 @@ public class TextSecureCipher {
}
return new TextSecureMessage(envelope.getTimestamp(), groupInfo, attachments,
content.getBody(), secure, endSession);
content.getBody(), syncContext, secure, endSession);
}
private TextSecureSyncContext createSyncContext(PushMessageContent content) {
if (!content.hasSync()) return null;
return new TextSecureSyncContext(content.getSync().getDestination(),
content.getSync().getTimestamp());
}
private TextSecureGroup createGroupInfo(TextSecureEnvelope envelope, PushMessageContent content) {

View File

@ -30,6 +30,7 @@ public class TextSecureMessage {
private final Optional<List<TextSecureAttachment>> attachments;
private final Optional<String> body;
private final Optional<TextSecureGroup> group;
private final Optional<TextSecureSyncContext> syncContext;
private final boolean secure;
private final boolean endSession;
@ -67,7 +68,7 @@ public class TextSecureMessage {
* @param body The message contents.
*/
public TextSecureMessage(long timestamp, TextSecureGroup group, List<TextSecureAttachment> attachments, String body) {
this(timestamp, group, attachments, body, true, false);
this(timestamp, group, attachments, body, null, true, false);
}
/**
@ -80,10 +81,11 @@ public class TextSecureMessage {
* @param secure Flag indicating whether this message is to be encrypted.
* @param endSession Flag indicating whether this message should close a session.
*/
public TextSecureMessage(long timestamp, TextSecureGroup group, List<TextSecureAttachment> attachments, String body, boolean secure, boolean endSession) {
public TextSecureMessage(long timestamp, TextSecureGroup group, List<TextSecureAttachment> attachments, String body, TextSecureSyncContext syncContext, boolean secure, boolean endSession) {
this.timestamp = timestamp;
this.body = Optional.fromNullable(body);
this.group = Optional.fromNullable(group);
this.syncContext = Optional.fromNullable(syncContext);
this.secure = secure;
this.endSession = endSession;
@ -126,6 +128,10 @@ public class TextSecureMessage {
return group;
}
public Optional<TextSecureSyncContext> getSyncContext() {
return syncContext;
}
public boolean isSecure() {
return secure;
}
@ -185,7 +191,7 @@ public class TextSecureMessage {
public TextSecureMessage build() {
if (timestamp == 0) timestamp = System.currentTimeMillis();
return new TextSecureMessage(timestamp, group, attachments, body, true, endSession);
return new TextSecureMessage(timestamp, group, attachments, body, null, true, endSession);
}
}
}

View File

@ -0,0 +1,20 @@
package org.whispersystems.textsecure.api.messages;
public class TextSecureSyncContext {
private final String destination;
private final long timestamp;
public TextSecureSyncContext(String destination, long timestamp) {
this.destination = destination;
this.timestamp = timestamp;
}
public String getDestination() {
return destination;
}
public long getTimestamp() {
return timestamp;
}
}

View File

@ -18,6 +18,7 @@ package org.whispersystems.textsecure.api.push.exceptions;
import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
import java.util.LinkedList;
import java.util.List;
public class EncapsulatedExceptions extends Throwable {
@ -35,6 +36,14 @@ public class EncapsulatedExceptions extends Throwable {
this.networkExceptions = networkExceptions;
}
public EncapsulatedExceptions(UntrustedIdentityException e) {
this.untrustedIdentityExceptions = new LinkedList<>();
this.unregisteredUserExceptions = new LinkedList<>();
this.networkExceptions = new LinkedList<>();
this.untrustedIdentityExceptions.add(e);
}
public List<UntrustedIdentityException> getUntrustedIdentityExceptions() {
return untrustedIdentityExceptions;
}