Skip to content

Commit

Permalink
Handle identity key mismatch on outgoing group messages.
Browse files Browse the repository at this point in the history
Additionally, make the group creation process asynchronous.
  • Loading branch information
moxie0 committed Feb 17, 2014
1 parent 5810062 commit b9f4fba
Show file tree
Hide file tree
Showing 19 changed files with 354 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import com.google.thoughtcrimegson.Gson;
import com.google.thoughtcrimegson.JsonParseException;
import com.google.thoughtcrimegson.JsonSyntaxException;

import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.whispersystems.textsecure.crypto.IdentityKey;
Expand All @@ -27,7 +26,6 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -94,7 +92,7 @@ public void sendMessage(OutgoingPushMessageList bundle)
try {
makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
} catch (NotFoundException nfe) {
throw new UnregisteredUserException(nfe);
throw new UnregisteredUserException(bundle.getDestination(), nfe);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@

public class UnregisteredUserException extends IOException {

public UnregisteredUserException(Exception exception) {
private final String e164number;

public UnregisteredUserException(String e164number, Exception exception) {
super(exception);
this.e164number = e164number;
}

public String getE164Number() {
return e164number;
}
}
8 changes: 7 additions & 1 deletion src/org/thoughtcrime/securesms/ConversationItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.thoughtcrime.securesms.util.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
Expand All @@ -42,6 +43,7 @@
import android.widget.Toast;
import android.webkit.MimeTypeMap;

import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
Expand All @@ -53,14 +55,17 @@
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.Emoji;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.FutureTaskListener;
import org.whispersystems.textsecure.util.ListenableFutureTask;
import org.whispersystems.textsecure.util.Util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;

Expand Down Expand Up @@ -172,13 +177,14 @@ public void setHandler(Handler failedIconHandler) {
/// MessageRecord Attribute Parsers

private void setBodyText(MessageRecord messageRecord) {
// TODO jake is going to fill these in
switch (messageRecord.getGroupAction()) {
case GroupContext.Type.QUIT_VALUE:
bodyText.setText(messageRecord.getIndividualRecipient().toShortString() + " has left the group.");
return;
case GroupContext.Type.ADD_VALUE:
case GroupContext.Type.CREATE_VALUE:
bodyText.setText(messageRecord.getGroupActionArguments() + " have joined the group.");
bodyText.setText(Util.join(GroupUtil.getSerializedArgumentMembers(messageRecord.getGroupActionArguments()), ", ") + " have joined the group.");
return;
case GroupContext.Type.MODIFY_VALUE:
bodyText.setText(messageRecord.getIndividualRecipient() + " has updated the group.");
Expand Down
72 changes: 22 additions & 50 deletions src/org/thoughtcrime/securesms/GroupCreateActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
Expand All @@ -30,8 +29,7 @@
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.transport.PushTransport;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
Expand All @@ -42,7 +40,7 @@
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.directory.NotInDirectoryException;
import org.whispersystems.textsecure.push.PushAttachmentPointer;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.InvalidNumberException;

import java.io.ByteArrayOutputStream;
Expand All @@ -54,8 +52,9 @@
import java.util.List;
import java.util.Set;

import ws.com.google.android.mms.MmsException;

import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;


Expand Down Expand Up @@ -355,59 +354,32 @@ public void onClick(View v) {
}
}

private Pair<Long, List<Recipient>> handleCreatePushGroup(String groupName,
byte[] avatar,
Set<Recipient> members)
private long handleCreatePushGroup(String groupName,
byte[] avatar,
Set<Recipient> members)
throws IOException, InvalidNumberException
{
List<String> memberE164Numbers = getE164Numbers(members);
PushTransport transport = new PushTransport(this, masterSecret);
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(this);
byte[] groupId = groupDatabase.allocateGroupId();
AttachmentPointer avatarPointer = null;

memberE164Numbers.add(TextSecurePreferences.getLocalNumber(this));

GroupContext.Builder builder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId))
.setType(GroupContext.Type.CREATE)
.setName(groupName)
.addAllMembers(memberE164Numbers);

if (avatar != null) {
PushAttachmentPointer pointer = transport.createAttachment("image/png", avatar);
avatarPointer = AttachmentPointer.newBuilder()
.setKey(ByteString.copyFrom(pointer.getKey()))
.setContentType(pointer.getContentType())
.setId(pointer.getId()).build();
builder.setAvatar(avatarPointer);
}
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
List<String> memberE164Numbers = getE164Numbers(members);
byte[] groupId = groupDatabase.allocateGroupId();
String groupRecipientId = GroupUtil.getEncodedId(groupId);

List<Recipient> failures = transport.deliver(new LinkedList<Recipient>(members), builder.build());
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
memberE164Numbers, avatarPointer, null);
String groupActionArguments = GroupUtil.serializeArguments(groupId, groupName, memberE164Numbers);

if (avatar != null) {
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
memberE164Numbers, null, null);
groupDatabase.updateAvatar(groupId, avatar);
}

try {
String groupRecipientId = GroupUtil.getEncodedId(groupId);
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false).getPrimaryRecipient();
OutgoingTextMessage outgoing = new OutgoingTextMessage(groupRecipient, GroupContext.Type.ADD_VALUE, org.whispersystems.textsecure.util.Util.join(memberE164Numbers, ","));
long threadId = threadDatabase.getThreadIdFor(new Recipients(groupRecipient));
List<Long> messageIds = DatabaseFactory.getEncryptingSmsDatabase(this)
.insertMessageOutbox(masterSecret, threadId, outgoing);

for (long messageId : messageIds) {
DatabaseFactory.getEncryptingSmsDatabase(this).markAsSent(messageId);
}

Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);

return new Pair<Long, List<Recipient>>(threadId, failures);
return MessageSender.sendGroupAction(this, masterSecret, groupRecipient, -1,
GroupContext.Type.CREATE_VALUE,
groupActionArguments, avatar);
} catch (RecipientFormattingException e) {
throw new AssertionError(e);
throw new IOException(e);
} catch (MmsException e) {
throw new IOException(e);
}
}

Expand Down
18 changes: 12 additions & 6 deletions src/org/thoughtcrime/securesms/database/MmsDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
CONTENT_LOCATION, EXPIRY, MESSAGE_CLASS, MESSAGE_TYPE, MMS_VERSION,
MESSAGE_SIZE, PRIORITY, REPORT_ALLOWED, STATUS, TRANSACTION_ID, RETRIEVE_STATUS,
RETRIEVE_TEXT, RETRIEVE_TEXT_CS, READ_STATUS, CONTENT_CLASS, RESPONSE_TEXT,
DELIVERY_TIME, DELIVERY_REPORT, BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID
DELIVERY_TIME, DELIVERY_REPORT, BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
GROUP_ACTION, GROUP_ACTION_ARGUMENTS
};

public static final ExecutorService slideResolver = org.thoughtcrime.securesms.util.Util.newSingleThreadedLifoExecutor();
Expand Down Expand Up @@ -343,10 +344,13 @@ public SendReq[] getOutgoingMessages(MasterSecret masterSecret, long messageId)
int i = 0;

while (cursor.moveToNext()) {
messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
PduHeaders headers = getHeadersFromCursor(cursor);
messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));

long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_ACTION));
String groupActionArguments = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ACTION_ARGUMENTS));
PduHeaders headers = getHeadersFromCursor(cursor);
addr.getAddressesForId(messageId, headers);

PduBody body = getPartsAsBody(partDatabase.getParts(messageId, true));
Expand All @@ -361,7 +365,7 @@ public SendReq[] getOutgoingMessages(MasterSecret masterSecret, long messageId)
Log.w("MmsDatabase", e);
}

requests[i++] = new SendReq(headers, body, messageId, outboxType);
requests[i++] = new SendReq(headers, body, messageId, outboxType, groupAction, groupActionArguments);
}

return requests;
Expand Down Expand Up @@ -514,6 +518,8 @@ public long insertMessageOutbox(MasterSecret masterSecret, SendReq sendRequest,
contentValues.put(THREAD_ID, threadId);
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
contentValues.put(GROUP_ACTION, sendRequest.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, sendRequest.getGroupActionArguments());
contentValues.remove(ADDRESS);

long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.Pair;

import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.push.PushMessageProtos;
import org.whispersystems.textsecure.util.Util;

import java.util.List;

import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;

/**
Expand Down Expand Up @@ -55,12 +59,13 @@ public ThreadRecord(Context context, Body body, Recipients recipients, long date

@Override
public SpannableString getDisplayBody() {
// TODO jake is going to fill these in
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
} else if (getGroupAction() == GroupContext.Type.ADD_VALUE ||
getGroupAction() == GroupContext.Type.CREATE_VALUE)
{
return emphasisAdded("Added " + getGroupActionArguments());
return emphasisAdded(Util.join(GroupUtil.getSerializedArgumentMembers(getGroupActionArguments()), ", ") + " have joined the group");
} else if (getGroupAction() == GroupContext.Type.QUIT_VALUE) {
return emphasisAdded(getRecipients().toShortString() + " left the group.");
} else if (getGroupAction() == GroupContext.Type.MODIFY_VALUE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public IncomingMediaMessage(MasterSecret masterSecret, String localNumber,
if (messageContent.hasGroup()) {
this.groupId = GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray());
this.groupAction = messageContent.getGroup().getType().getNumber();
this.groupActionArguments = GroupUtil.getActionArgument(messageContent.getGroup());
this.groupActionArguments = GroupUtil.serializeArguments(messageContent.getGroup());
} else {
this.groupId = null;
this.groupAction = -1;
Expand Down
3 changes: 1 addition & 2 deletions src/org/thoughtcrime/securesms/service/MmsSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ private void handleSendMms(MasterSecret masterSecret, Intent intent) {
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
} catch (UntrustedIdentityException uie) {
IncomingTextMessage base = new IncomingTextMessage(message);
IncomingIdentityUpdateMessage identityUpdateMessage = new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(uie.getIdentityKey().serialize()));
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(message.getTo()[0].getString(), uie.getIdentityKey());
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
database.markAsSentFailed(messageId);
} catch (RetryLaterException e) {
Expand Down
3 changes: 1 addition & 2 deletions src/org/thoughtcrime/securesms/service/SmsSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ private void handleSendMessage(MasterSecret masterSecret, Intent intent) {
transport.deliver(record);
} catch (UntrustedIdentityException e) {
Log.w("SmsSender", e);
IncomingTextMessage base = new IncomingTextMessage(record);
IncomingIdentityUpdateMessage identityUpdateMessage = new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(e.getIdentityKey().serialize()));
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(e.getE164Number(), e.getIdentityKey());
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
} catch (UndeliverableMessageException ude) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.thoughtcrime.securesms.sms;

import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.util.Base64;

public class IncomingIdentityUpdateMessage extends IncomingKeyExchangeMessage {

public IncomingIdentityUpdateMessage(IncomingTextMessage base, String newBody) {
Expand All @@ -15,4 +18,13 @@ public IncomingIdentityUpdateMessage withMessageBody(String messageBody) {
public boolean isIdentityUpdate() {
return true;
}

public static IncomingIdentityUpdateMessage createFor(String sender, IdentityKey identityKey) {
return createFor(sender, identityKey, null);
}

public static IncomingIdentityUpdateMessage createFor(String sender, IdentityKey identityKey, String groupId) {
IncomingTextMessage base = new IncomingTextMessage(sender, groupId, -1, null);
return new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(identityKey.serialize()));
}
}
22 changes: 21 additions & 1 deletion src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public IncomingTextMessage(IncomingPushMessage message, String encodedBody, Grou
if (group != null) {
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
this.groupAction = group.getType().getNumber();
this.groupActionArgument = GroupUtil.getActionArgument(group);
this.groupActionArgument = GroupUtil.serializeArguments(group);
} else {
this.groupId = null;
this.groupAction = -1;
Expand Down Expand Up @@ -152,6 +152,22 @@ public IncomingTextMessage(SmsMessageRecord record) {
this.groupActionArgument = null;
}

protected IncomingTextMessage(String sender, String groupId,
int groupAction, String groupActionArgument)
{
this.message = "";
this.sender = sender;
this.senderDeviceId = RecipientDevice.DEFAULT_DEVICE_ID;
this.protocol = 31338;
this.serviceCenterAddress = "Outgoing";
this.replyPathPresent = true;
this.pseudoSubject = "";
this.sentTimestampMillis = System.currentTimeMillis();
this.groupId = groupId;
this.groupAction = groupAction;
this.groupActionArgument = groupActionArgument;
}

public long getSentTimestampMillis() {
return sentTimestampMillis;
}
Expand Down Expand Up @@ -235,4 +251,8 @@ public void writeToParcel(Parcel out, int flags) {
out.writeInt(groupAction);
out.writeString(groupActionArgument);
}

public static IncomingTextMessage createForLeavingGroup(String groupId, String user) {
return new IncomingTextMessage(user, groupId, GroupContext.Type.QUIT_VALUE, null);
}
}

0 comments on commit b9f4fba

Please sign in to comment.