mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Handle identity key mismatch on outgoing group messages.
Additionally, make the group creation process asynchronous.
This commit is contained in:
parent
5810062b25
commit
b9f4fba98a
@ -5,7 +5,6 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.google.thoughtcrimegson.Gson;
|
import com.google.thoughtcrimegson.Gson;
|
||||||
import com.google.thoughtcrimegson.JsonParseException;
|
import com.google.thoughtcrimegson.JsonParseException;
|
||||||
import com.google.thoughtcrimegson.JsonSyntaxException;
|
|
||||||
|
|
||||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||||
@ -27,7 +26,6 @@ import java.security.KeyStore;
|
|||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -94,7 +92,7 @@ public class PushServiceSocket {
|
|||||||
try {
|
try {
|
||||||
makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
|
makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
|
||||||
} catch (NotFoundException nfe) {
|
} catch (NotFoundException nfe) {
|
||||||
throw new UnregisteredUserException(nfe);
|
throw new UnregisteredUserException(bundle.getDestination(), nfe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,14 @@ import java.util.List;
|
|||||||
|
|
||||||
public class UnregisteredUserException extends IOException {
|
public class UnregisteredUserException extends IOException {
|
||||||
|
|
||||||
public UnregisteredUserException(Exception exception) {
|
private final String e164number;
|
||||||
|
|
||||||
|
public UnregisteredUserException(String e164number, Exception exception) {
|
||||||
super(exception);
|
super(exception);
|
||||||
|
this.e164number = e164number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getE164Number() {
|
||||||
|
return e164number;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import android.provider.ContactsContract.QuickContact;
|
|||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -42,6 +43,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
@ -53,14 +55,17 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||||||
import org.thoughtcrime.securesms.service.SendReceiveService;
|
import org.thoughtcrime.securesms.service.SendReceiveService;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
import org.thoughtcrime.securesms.util.Emoji;
|
||||||
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
import org.whispersystems.textsecure.util.FutureTaskListener;
|
import org.whispersystems.textsecure.util.FutureTaskListener;
|
||||||
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
||||||
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
@ -172,13 +177,14 @@ public class ConversationItem extends LinearLayout {
|
|||||||
/// MessageRecord Attribute Parsers
|
/// MessageRecord Attribute Parsers
|
||||||
|
|
||||||
private void setBodyText(MessageRecord messageRecord) {
|
private void setBodyText(MessageRecord messageRecord) {
|
||||||
|
// TODO jake is going to fill these in
|
||||||
switch (messageRecord.getGroupAction()) {
|
switch (messageRecord.getGroupAction()) {
|
||||||
case GroupContext.Type.QUIT_VALUE:
|
case GroupContext.Type.QUIT_VALUE:
|
||||||
bodyText.setText(messageRecord.getIndividualRecipient().toShortString() + " has left the group.");
|
bodyText.setText(messageRecord.getIndividualRecipient().toShortString() + " has left the group.");
|
||||||
return;
|
return;
|
||||||
case GroupContext.Type.ADD_VALUE:
|
case GroupContext.Type.ADD_VALUE:
|
||||||
case GroupContext.Type.CREATE_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;
|
return;
|
||||||
case GroupContext.Type.MODIFY_VALUE:
|
case GroupContext.Type.MODIFY_VALUE:
|
||||||
bodyText.setText(messageRecord.getIndividualRecipient() + " has updated the group.");
|
bodyText.setText(messageRecord.getIndividualRecipient() + " has updated the group.");
|
||||||
|
@ -9,7 +9,6 @@ import android.os.Bundle;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -30,8 +29,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.transport.PushTransport;
|
|
||||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
@ -42,7 +40,7 @@ import org.thoughtcrime.securesms.util.Util;
|
|||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.whispersystems.textsecure.directory.Directory;
|
import org.whispersystems.textsecure.directory.Directory;
|
||||||
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
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 org.whispersystems.textsecure.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -54,8 +52,9 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ws.com.google.android.mms.MmsException;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
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;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
|
|
||||||
@ -355,59 +354,32 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, List<Recipient>> handleCreatePushGroup(String groupName,
|
private long handleCreatePushGroup(String groupName,
|
||||||
byte[] avatar,
|
byte[] avatar,
|
||||||
Set<Recipient> members)
|
Set<Recipient> members)
|
||||||
throws IOException, InvalidNumberException
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Recipient> failures = transport.deliver(new LinkedList<Recipient>(members), builder.build());
|
|
||||||
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
|
||||||
memberE164Numbers, avatarPointer, null);
|
|
||||||
|
|
||||||
if (avatar != null) {
|
|
||||||
groupDatabase.updateAvatar(groupId, avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
||||||
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false).getPrimaryRecipient();
|
List<String> memberE164Numbers = getE164Numbers(members);
|
||||||
OutgoingTextMessage outgoing = new OutgoingTextMessage(groupRecipient, GroupContext.Type.ADD_VALUE, org.whispersystems.textsecure.util.Util.join(memberE164Numbers, ","));
|
byte[] groupId = groupDatabase.allocateGroupId();
|
||||||
long threadId = threadDatabase.getThreadIdFor(new Recipients(groupRecipient));
|
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
||||||
List<Long> messageIds = DatabaseFactory.getEncryptingSmsDatabase(this)
|
|
||||||
.insertMessageOutbox(masterSecret, threadId, outgoing);
|
|
||||||
|
|
||||||
for (long messageId : messageIds) {
|
String groupActionArguments = GroupUtil.serializeArguments(groupId, groupName, memberE164Numbers);
|
||||||
DatabaseFactory.getEncryptingSmsDatabase(this).markAsSent(messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
||||||
|
memberE164Numbers, null, null);
|
||||||
|
groupDatabase.updateAvatar(groupId, avatar);
|
||||||
|
|
||||||
return new Pair<Long, List<Recipient>>(threadId, failures);
|
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
|
||||||
|
|
||||||
|
return MessageSender.sendGroupAction(this, masterSecret, groupRecipient, -1,
|
||||||
|
GroupContext.Type.CREATE_VALUE,
|
||||||
|
groupActionArguments, avatar);
|
||||||
} catch (RecipientFormattingException e) {
|
} catch (RecipientFormattingException e) {
|
||||||
throw new AssertionError(e);
|
throw new IOException(e);
|
||||||
|
} catch (MmsException e) {
|
||||||
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
CONTENT_LOCATION, EXPIRY, MESSAGE_CLASS, MESSAGE_TYPE, MMS_VERSION,
|
CONTENT_LOCATION, EXPIRY, MESSAGE_CLASS, MESSAGE_TYPE, MMS_VERSION,
|
||||||
MESSAGE_SIZE, PRIORITY, REPORT_ALLOWED, STATUS, TRANSACTION_ID, RETRIEVE_STATUS,
|
MESSAGE_SIZE, PRIORITY, REPORT_ALLOWED, STATUS, TRANSACTION_ID, RETRIEVE_STATUS,
|
||||||
RETRIEVE_TEXT, RETRIEVE_TEXT_CS, READ_STATUS, CONTENT_CLASS, RESPONSE_TEXT,
|
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();
|
public static final ExecutorService slideResolver = org.thoughtcrime.securesms.util.Util.newSingleThreadedLifoExecutor();
|
||||||
@ -343,10 +344,13 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
|
|
||||||
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
|
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
|
||||||
PduHeaders headers = getHeadersFromCursor(cursor);
|
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);
|
addr.getAddressesForId(messageId, headers);
|
||||||
|
|
||||||
PduBody body = getPartsAsBody(partDatabase.getParts(messageId, true));
|
PduBody body = getPartsAsBody(partDatabase.getParts(messageId, true));
|
||||||
@ -361,7 +365,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
Log.w("MmsDatabase", e);
|
Log.w("MmsDatabase", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
requests[i++] = new SendReq(headers, body, messageId, outboxType);
|
requests[i++] = new SendReq(headers, body, messageId, outboxType, groupAction, groupActionArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
return requests;
|
return requests;
|
||||||
@ -514,6 +518,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
contentValues.put(THREAD_ID, threadId);
|
contentValues.put(THREAD_ID, threadId);
|
||||||
contentValues.put(READ, 1);
|
contentValues.put(READ, 1);
|
||||||
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
|
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
|
||||||
|
contentValues.put(GROUP_ACTION, sendRequest.getGroupAction());
|
||||||
|
contentValues.put(GROUP_ACTION_ARGUMENTS, sendRequest.getGroupActionArguments());
|
||||||
contentValues.remove(ADDRESS);
|
contentValues.remove(ADDRESS);
|
||||||
|
|
||||||
long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
|
long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
|
||||||
|
@ -20,13 +20,17 @@ import android.content.Context;
|
|||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.textsecure.push.PushMessageProtos;
|
import org.whispersystems.textsecure.push.PushMessageProtos;
|
||||||
import org.whispersystems.textsecure.util.Util;
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,12 +59,13 @@ public class ThreadRecord extends DisplayRecord {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody() {
|
public SpannableString getDisplayBody() {
|
||||||
|
// TODO jake is going to fill these in
|
||||||
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
|
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
|
||||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
|
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
|
||||||
} else if (getGroupAction() == GroupContext.Type.ADD_VALUE ||
|
} else if (getGroupAction() == GroupContext.Type.ADD_VALUE ||
|
||||||
getGroupAction() == GroupContext.Type.CREATE_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) {
|
} else if (getGroupAction() == GroupContext.Type.QUIT_VALUE) {
|
||||||
return emphasisAdded(getRecipients().toShortString() + " left the group.");
|
return emphasisAdded(getRecipients().toShortString() + " left the group.");
|
||||||
} else if (getGroupAction() == GroupContext.Type.MODIFY_VALUE) {
|
} else if (getGroupAction() == GroupContext.Type.MODIFY_VALUE) {
|
||||||
|
@ -41,7 +41,7 @@ public class IncomingMediaMessage {
|
|||||||
if (messageContent.hasGroup()) {
|
if (messageContent.hasGroup()) {
|
||||||
this.groupId = GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray());
|
this.groupId = GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray());
|
||||||
this.groupAction = messageContent.getGroup().getType().getNumber();
|
this.groupAction = messageContent.getGroup().getType().getNumber();
|
||||||
this.groupActionArguments = GroupUtil.getActionArgument(messageContent.getGroup());
|
this.groupActionArguments = GroupUtil.serializeArguments(messageContent.getGroup());
|
||||||
} else {
|
} else {
|
||||||
this.groupId = null;
|
this.groupId = null;
|
||||||
this.groupAction = -1;
|
this.groupAction = -1;
|
||||||
|
@ -92,8 +92,7 @@ public class MmsSender {
|
|||||||
Recipients recipients = threads.getRecipientsForThreadId(threadId);
|
Recipients recipients = threads.getRecipientsForThreadId(threadId);
|
||||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
||||||
} catch (UntrustedIdentityException uie) {
|
} catch (UntrustedIdentityException uie) {
|
||||||
IncomingTextMessage base = new IncomingTextMessage(message);
|
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(message.getTo()[0].getString(), uie.getIdentityKey());
|
||||||
IncomingIdentityUpdateMessage identityUpdateMessage = new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(uie.getIdentityKey().serialize()));
|
|
||||||
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
|
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
|
||||||
database.markAsSentFailed(messageId);
|
database.markAsSentFailed(messageId);
|
||||||
} catch (RetryLaterException e) {
|
} catch (RetryLaterException e) {
|
||||||
|
@ -81,8 +81,7 @@ public class SmsSender {
|
|||||||
transport.deliver(record);
|
transport.deliver(record);
|
||||||
} catch (UntrustedIdentityException e) {
|
} catch (UntrustedIdentityException e) {
|
||||||
Log.w("SmsSender", e);
|
Log.w("SmsSender", e);
|
||||||
IncomingTextMessage base = new IncomingTextMessage(record);
|
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(e.getE164Number(), e.getIdentityKey());
|
||||||
IncomingIdentityUpdateMessage identityUpdateMessage = new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(e.getIdentityKey().serialize()));
|
|
||||||
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
|
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||||
} catch (UndeliverableMessageException ude) {
|
} catch (UndeliverableMessageException ude) {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package org.thoughtcrime.securesms.sms;
|
package org.thoughtcrime.securesms.sms;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||||
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
|
|
||||||
public class IncomingIdentityUpdateMessage extends IncomingKeyExchangeMessage {
|
public class IncomingIdentityUpdateMessage extends IncomingKeyExchangeMessage {
|
||||||
|
|
||||||
public IncomingIdentityUpdateMessage(IncomingTextMessage base, String newBody) {
|
public IncomingIdentityUpdateMessage(IncomingTextMessage base, String newBody) {
|
||||||
@ -15,4 +18,13 @@ public class IncomingIdentityUpdateMessage extends IncomingKeyExchangeMessage {
|
|||||||
public boolean isIdentityUpdate() {
|
public boolean isIdentityUpdate() {
|
||||||
return true;
|
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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
if (group != null) {
|
if (group != null) {
|
||||||
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
|
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
|
||||||
this.groupAction = group.getType().getNumber();
|
this.groupAction = group.getType().getNumber();
|
||||||
this.groupActionArgument = GroupUtil.getActionArgument(group);
|
this.groupActionArgument = GroupUtil.serializeArguments(group);
|
||||||
} else {
|
} else {
|
||||||
this.groupId = null;
|
this.groupId = null;
|
||||||
this.groupAction = -1;
|
this.groupAction = -1;
|
||||||
@ -152,6 +152,22 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.groupActionArgument = null;
|
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() {
|
public long getSentTimestampMillis() {
|
||||||
return sentTimestampMillis;
|
return sentTimestampMillis;
|
||||||
}
|
}
|
||||||
@ -235,4 +251,8 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
out.writeInt(groupAction);
|
out.writeInt(groupAction);
|
||||||
out.writeString(groupActionArgument);
|
out.writeString(groupActionArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IncomingTextMessage createForLeavingGroup(String groupId, String user) {
|
||||||
|
return new IncomingTextMessage(user, groupId, GroupContext.Type.QUIT_VALUE, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
@ -34,10 +35,43 @@ import ws.com.google.android.mms.ContentType;
|
|||||||
import ws.com.google.android.mms.MmsException;
|
import ws.com.google.android.mms.MmsException;
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
import ws.com.google.android.mms.pdu.PduBody;
|
import ws.com.google.android.mms.pdu.PduBody;
|
||||||
|
import ws.com.google.android.mms.pdu.PduPart;
|
||||||
import ws.com.google.android.mms.pdu.SendReq;
|
import ws.com.google.android.mms.pdu.SendReq;
|
||||||
|
|
||||||
public class MessageSender {
|
public class MessageSender {
|
||||||
|
|
||||||
|
public static long sendGroupAction(Context context, MasterSecret masterSecret, Recipients recipients,
|
||||||
|
long threadId, int groupAction, String groupActionArguments, byte[] avatar)
|
||||||
|
throws MmsException
|
||||||
|
{
|
||||||
|
if (threadId == -1) {
|
||||||
|
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
PduBody body = new PduBody();
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
PduPart part = new PduPart();
|
||||||
|
part.setData(avatar);
|
||||||
|
part.setContentType(ContentType.IMAGE_PNG.getBytes());
|
||||||
|
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||||
|
part.setName(("Image" + System.currentTimeMillis()).getBytes());
|
||||||
|
body.addPart(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendReq sendRequest = new SendReq();
|
||||||
|
sendRequest.setDate(System.currentTimeMillis() / 1000L);
|
||||||
|
sendRequest.setBody(body);
|
||||||
|
sendRequest.setContentType(ContentType.MULTIPART_MIXED.getBytes());
|
||||||
|
sendRequest.setGroupAction(groupAction);
|
||||||
|
sendRequest.setGroupActionArguments(groupActionArguments);
|
||||||
|
|
||||||
|
sendMms(context, recipients, masterSecret, sendRequest, threadId,
|
||||||
|
ThreadDatabase.DistributionTypes.CONVERSATION, true);
|
||||||
|
|
||||||
|
return threadId;
|
||||||
|
}
|
||||||
|
|
||||||
public static long sendMms(Context context, MasterSecret masterSecret, Recipients recipients,
|
public static long sendMms(Context context, MasterSecret masterSecret, Recipients recipients,
|
||||||
long threadId, SlideDeck slideDeck, String message, int distributionType,
|
long threadId, SlideDeck slideDeck, String message, int distributionType,
|
||||||
boolean secure)
|
boolean secure)
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.thoughtcrime.securesms.transport;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecure.push.UnregisteredUserException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EncapsulatedExceptions extends Throwable {
|
||||||
|
|
||||||
|
private final List<UntrustedIdentityException> untrustedIdentityExceptions;
|
||||||
|
private final List<UnregisteredUserException> unregisteredUserExceptions;
|
||||||
|
|
||||||
|
public EncapsulatedExceptions(List<UntrustedIdentityException> untrustedIdentities,
|
||||||
|
List<UnregisteredUserException> unregisteredUsers)
|
||||||
|
{
|
||||||
|
this.untrustedIdentityExceptions = untrustedIdentities;
|
||||||
|
this.unregisteredUserExceptions = unregisteredUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UntrustedIdentityException> getUntrustedIdentityExceptions() {
|
||||||
|
return untrustedIdentityExceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UnregisteredUserException> getUnregisteredUserExceptions() {
|
||||||
|
return unregisteredUserExceptions;
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,7 @@ import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
|||||||
import org.whispersystems.textsecure.push.PushServiceSocket;
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
import org.whispersystems.textsecure.push.UnregisteredUserException;
|
import org.whispersystems.textsecure.push.UnregisteredUserException;
|
||||||
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
||||||
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
import org.whispersystems.textsecure.util.InvalidNumberException;
|
import org.whispersystems.textsecure.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -62,6 +63,8 @@ import ws.com.google.android.mms.pdu.PduBody;
|
|||||||
import ws.com.google.android.mms.pdu.SendReq;
|
import ws.com.google.android.mms.pdu.SendReq;
|
||||||
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
public class PushTransport extends BaseTransport {
|
public class PushTransport extends BaseTransport {
|
||||||
|
|
||||||
@ -100,7 +103,7 @@ public class PushTransport extends BaseTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deliver(SendReq message, long threadId)
|
public void deliver(SendReq message, long threadId)
|
||||||
throws IOException, UntrustedIdentityException
|
throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions
|
||||||
{
|
{
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
byte[] plaintext = getPlaintextMessage(socket, message);
|
byte[] plaintext = getPlaintextMessage(socket, message);
|
||||||
@ -108,67 +111,31 @@ public class PushTransport extends BaseTransport {
|
|||||||
|
|
||||||
Recipients recipients;
|
Recipients recipients;
|
||||||
|
|
||||||
try {
|
if (GroupUtil.isEncodedGroup(destination)) {
|
||||||
if (GroupUtil.isEncodedGroup(destination)) {
|
recipients = DatabaseFactory.getGroupDatabase(context)
|
||||||
recipients = DatabaseFactory.getGroupDatabase(context)
|
.getGroupMembers(GroupUtil.getDecodedId(destination));
|
||||||
.getGroupMembers(GroupUtil.getDecodedId(destination));
|
} else {
|
||||||
} else {
|
recipients = RecipientFactory.getRecipientsFromString(context, destination, false);
|
||||||
recipients = RecipientFactory.getRecipientsFromString(context, destination, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Recipient recipient : recipients.getRecipientsList()) {
|
|
||||||
deliver(socket, recipient, threadId, plaintext);
|
|
||||||
}
|
|
||||||
} catch (UnregisteredUserException uue) {
|
|
||||||
// TODO: We should probably remove the user from the directory?
|
|
||||||
throw new IOException(uue);
|
|
||||||
} catch (RecipientFormattingException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
} catch (InvalidNumberException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public List<Recipient> deliver(List<Recipient> recipients,
|
List<UntrustedIdentityException> untrustedIdentities = new LinkedList<UntrustedIdentityException>();
|
||||||
PushMessageContent.GroupContext groupAction)
|
List<UnregisteredUserException> unregisteredUsers = new LinkedList<UnregisteredUserException>();
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
|
||||||
byte[] plaintext = PushMessageContent.newBuilder()
|
|
||||||
.setGroup(groupAction)
|
|
||||||
.build().toByteArray();
|
|
||||||
List<Recipient> failures = new LinkedList<Recipient>();
|
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients.getRecipientsList()) {
|
||||||
try {
|
try {
|
||||||
deliver(socket, recipient, -1, plaintext);
|
deliver(socket, recipient, threadId, plaintext);
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
Log.w("PushTransport", e);
|
|
||||||
failures.add(recipient);
|
|
||||||
} catch (InvalidNumberException e) {
|
|
||||||
Log.w("PushTransport", e);
|
|
||||||
failures.add(recipient);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w("PushTransport", e);
|
|
||||||
failures.add(recipient);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
} catch (UntrustedIdentityException e) {
|
||||||
Log.w("PushTransport", e);
|
Log.w("PushTransport", e);
|
||||||
failures.add(recipient);
|
untrustedIdentities.add(e);
|
||||||
|
} catch (UnregisteredUserException e) {
|
||||||
|
Log.w("PushTransport", e);
|
||||||
|
unregisteredUsers.add(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failures.size() == recipients.size()) {
|
if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty()) {
|
||||||
throw new IOException("Total failure.");
|
throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
return failures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PushAttachmentPointer createAttachment(String contentType, byte[] data)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
|
||||||
return getPushAttachmentPointer(socket, contentType, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deliver(PushServiceSocket socket, Recipient recipient, long threadId, byte[] plaintext)
|
private void deliver(PushServiceSocket socket, Recipient recipient, long threadId, byte[] plaintext)
|
||||||
@ -252,11 +219,40 @@ public class PushTransport extends BaseTransport {
|
|||||||
PushMessageContent.Builder builder = PushMessageContent.newBuilder();
|
PushMessageContent.Builder builder = PushMessageContent.newBuilder();
|
||||||
|
|
||||||
if (GroupUtil.isEncodedGroup(message.getTo()[0].getString())) {
|
if (GroupUtil.isEncodedGroup(message.getTo()[0].getString())) {
|
||||||
PushMessageContent.GroupContext.Builder groupBuilder =
|
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
|
||||||
PushMessageContent.GroupContext.newBuilder();
|
GroupContext.Builder groupBuilder = GroupContext.newBuilder();
|
||||||
|
|
||||||
groupBuilder.setType(PushMessageContent.GroupContext.Type.DELIVER);
|
groupBuilder.setId(ByteString.copyFrom(groupId));
|
||||||
groupBuilder.setId(ByteString.copyFrom(GroupUtil.getDecodedId(message.getTo()[0].getString())));
|
|
||||||
|
switch (message.getGroupAction()) {
|
||||||
|
case GroupContext.Type.ADD_VALUE: groupBuilder.setType(GroupContext.Type.ADD); break;
|
||||||
|
case GroupContext.Type.CREATE_VALUE: groupBuilder.setType(GroupContext.Type.CREATE); break;
|
||||||
|
case GroupContext.Type.QUIT_VALUE: groupBuilder.setType(GroupContext.Type.QUIT); break;
|
||||||
|
default: groupBuilder.setType(GroupContext.Type.DELIVER); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.getGroupAction() == GroupContext.Type.ADD_VALUE ||
|
||||||
|
message.getGroupAction() == GroupContext.Type.CREATE_VALUE)
|
||||||
|
{
|
||||||
|
GroupContext serialized = GroupContext.parseFrom(Base64.decode(message.getGroupActionArguments()));
|
||||||
|
groupBuilder.addAllMembers(serialized.getMembersList());
|
||||||
|
|
||||||
|
if (serialized.hasName()) {
|
||||||
|
groupBuilder.setName(serialized.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.getGroupAction() == GroupContext.Type.CREATE_VALUE && !attachments.isEmpty()) {
|
||||||
|
Log.w("PushTransport", "Adding avatar...");
|
||||||
|
groupBuilder.setAvatar(AttachmentPointer.newBuilder()
|
||||||
|
.setId(attachments.get(0).getId())
|
||||||
|
.setContentType(attachments.get(0).getContentType())
|
||||||
|
.setKey(ByteString.copyFrom(attachments.get(0).getKey()))
|
||||||
|
.build());
|
||||||
|
attachments.remove(0);
|
||||||
|
} else {
|
||||||
|
Log.w("PushTransport", "Not adding avatar: " + message.getGroupAction() + " , " + attachments.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
builder.setGroup(groupBuilder.build());
|
builder.setGroup(groupBuilder.build());
|
||||||
}
|
}
|
||||||
@ -266,8 +262,8 @@ public class PushTransport extends BaseTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (PushAttachmentPointer attachment : attachments) {
|
for (PushAttachmentPointer attachment : attachments) {
|
||||||
PushMessageContent.AttachmentPointer.Builder attachmentBuilder =
|
AttachmentPointer.Builder attachmentBuilder =
|
||||||
PushMessageContent.AttachmentPointer.newBuilder();
|
AttachmentPointer.newBuilder();
|
||||||
|
|
||||||
attachmentBuilder.setId(attachment.getId());
|
attachmentBuilder.setId(attachment.getId());
|
||||||
attachmentBuilder.setContentType(attachment.getContentType());
|
attachmentBuilder.setContentType(attachment.getContentType());
|
||||||
@ -316,7 +312,7 @@ public class PushTransport extends BaseTransport {
|
|||||||
if (processor.isTrusted(preKey)) {
|
if (processor.isTrusted(preKey)) {
|
||||||
processor.processKeyExchangeMessage(preKey, threadId);
|
processor.processKeyExchangeMessage(preKey, threadId);
|
||||||
} else {
|
} else {
|
||||||
throw new UntrustedIdentityException("Untrusted identity key!", preKey.getIdentityKey());
|
throw new UntrustedIdentityException("Untrusted identity key!", pushAddress.getNumber(), preKey.getIdentityKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.transport;
|
package org.thoughtcrime.securesms.transport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class RetryLaterException extends Exception {
|
public class RetryLaterException extends Exception {
|
||||||
|
public RetryLaterException(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,14 @@ package org.thoughtcrime.securesms.transport;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
||||||
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||||
|
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
|
||||||
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -31,7 +35,11 @@ import org.whispersystems.textsecure.directory.Directory;
|
|||||||
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
||||||
import org.whispersystems.textsecure.push.ContactNumberDetails;
|
import org.whispersystems.textsecure.push.ContactNumberDetails;
|
||||||
import org.whispersystems.textsecure.push.ContactTokenDetails;
|
import org.whispersystems.textsecure.push.ContactTokenDetails;
|
||||||
|
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||||
|
import org.whispersystems.textsecure.push.PushMessageProtos;
|
||||||
import org.whispersystems.textsecure.push.PushServiceSocket;
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
|
import org.whispersystems.textsecure.push.UnregisteredUserException;
|
||||||
|
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||||
import org.whispersystems.textsecure.util.DirectoryUtil;
|
import org.whispersystems.textsecure.util.DirectoryUtil;
|
||||||
import org.whispersystems.textsecure.util.InvalidNumberException;
|
import org.whispersystems.textsecure.util.InvalidNumberException;
|
||||||
|
|
||||||
@ -39,15 +47,19 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import ws.com.google.android.mms.pdu.SendReq;
|
import ws.com.google.android.mms.pdu.SendReq;
|
||||||
|
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
||||||
|
|
||||||
public class UniversalTransport {
|
public class UniversalTransport {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private final MasterSecret masterSecret;
|
||||||
private final PushTransport pushTransport;
|
private final PushTransport pushTransport;
|
||||||
private final SmsTransport smsTransport;
|
private final SmsTransport smsTransport;
|
||||||
private final MmsTransport mmsTransport;
|
private final MmsTransport mmsTransport;
|
||||||
|
|
||||||
public UniversalTransport(Context context, MasterSecret masterSecret) {
|
public UniversalTransport(Context context, MasterSecret masterSecret) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
this.pushTransport = new PushTransport(context, masterSecret);
|
this.pushTransport = new PushTransport(context, masterSecret);
|
||||||
this.smsTransport = new SmsTransport(context, masterSecret);
|
this.smsTransport = new SmsTransport(context, masterSecret);
|
||||||
this.mmsTransport = new MmsTransport(context, masterSecret);
|
this.mmsTransport = new MmsTransport(context, masterSecret);
|
||||||
@ -90,6 +102,10 @@ public class UniversalTransport {
|
|||||||
throw new UndeliverableMessageException("No destination specified");
|
throw new UndeliverableMessageException("No destination specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GroupUtil.isEncodedGroup(mediaMessage.getTo()[0].getString())) {
|
||||||
|
return deliverGroupMessage(mediaMessage, threadId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||||
return mmsTransport.deliver(mediaMessage);
|
return mmsTransport.deliver(mediaMessage);
|
||||||
}
|
}
|
||||||
@ -98,34 +114,77 @@ public class UniversalTransport {
|
|||||||
return mmsTransport.deliver(mediaMessage);
|
return mmsTransport.deliver(mediaMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
String destination;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
destination = Util.canonicalizeNumber(context, mediaMessage.getTo()[0].getString());
|
String destination = Util.canonicalizeNumber(context, mediaMessage.getTo()[0].getString());
|
||||||
|
|
||||||
|
if (isPushTransport(destination)) {
|
||||||
|
try {
|
||||||
|
Log.w("UniversalTransport", "Delivering media message with GCM...");
|
||||||
|
pushTransport.deliver(mediaMessage, threadId);
|
||||||
|
return new MmsSendResult("push".getBytes("UTF-8"), 0, true);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w("UniversalTransport", ioe);
|
||||||
|
return mmsTransport.deliver(mediaMessage);
|
||||||
|
} catch (RecipientFormattingException e) {
|
||||||
|
Log.w("UniversalTransport", e);
|
||||||
|
return mmsTransport.deliver(mediaMessage);
|
||||||
|
} catch (EncapsulatedExceptions ee) {
|
||||||
|
Log.w("UniversalTransport", ee);
|
||||||
|
if (!ee.getUnregisteredUserExceptions().isEmpty()) {
|
||||||
|
return mmsTransport.deliver(mediaMessage);
|
||||||
|
} else {
|
||||||
|
throw new UntrustedIdentityException(ee.getUntrustedIdentityExceptions().get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w("UniversalTransport", "Delivering media message with MMS...");
|
||||||
|
return mmsTransport.deliver(mediaMessage);
|
||||||
|
}
|
||||||
} catch (InvalidNumberException ine) {
|
} catch (InvalidNumberException ine) {
|
||||||
Log.w("UniversalTransport", ine);
|
Log.w("UniversalTransport", ine);
|
||||||
return mmsTransport.deliver(mediaMessage);
|
return mmsTransport.deliver(mediaMessage);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isPushTransport(destination)) {
|
private MmsSendResult deliverGroupMessage(SendReq mediaMessage, long threadId)
|
||||||
|
throws RetryLaterException, UndeliverableMessageException
|
||||||
|
{
|
||||||
|
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||||
|
throw new UndeliverableMessageException("Not push registered!");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pushTransport.deliver(mediaMessage, threadId);
|
||||||
|
return new MmsSendResult("push".getBytes("UTF-8"), 0, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("UniversalTransport", e);
|
||||||
|
throw new RetryLaterException(e);
|
||||||
|
} catch (RecipientFormattingException e) {
|
||||||
|
throw new UndeliverableMessageException(e);
|
||||||
|
} catch (InvalidNumberException e) {
|
||||||
|
throw new UndeliverableMessageException(e);
|
||||||
|
} catch (EncapsulatedExceptions ee) {
|
||||||
|
Log.w("UniversalTransport", ee);
|
||||||
try {
|
try {
|
||||||
Log.w("UniversalTransport", "Delivering media message with GCM...");
|
for (UnregisteredUserException unregistered : ee.getUnregisteredUserExceptions()) {
|
||||||
pushTransport.deliver(mediaMessage, threadId);
|
IncomingTextMessage quitMessage = IncomingTextMessage.createForLeavingGroup(mediaMessage.getTo()[0].getString(), unregistered.getE164Number());
|
||||||
|
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, quitMessage);
|
||||||
|
DatabaseFactory.getGroupDatabase(context).remove(GroupUtil.getDecodedId(mediaMessage.getTo()[0].getString()), unregistered.getE164Number());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UntrustedIdentityException untrusted : ee.getUntrustedIdentityExceptions()) {
|
||||||
|
IncomingIdentityUpdateMessage identityMessage = IncomingIdentityUpdateMessage.createFor(untrusted.getE164Number(), untrusted.getIdentityKey(), mediaMessage.getTo()[0].getString());
|
||||||
|
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityMessage);
|
||||||
|
}
|
||||||
|
|
||||||
return new MmsSendResult("push".getBytes("UTF-8"), 0, true);
|
return new MmsSendResult("push".getBytes("UTF-8"), 0, true);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.w("UniversalTransport", ioe);
|
throw new AssertionError(ioe);
|
||||||
if (!GroupUtil.isEncodedGroup(destination)) {
|
|
||||||
return mmsTransport.deliver(mediaMessage);
|
|
||||||
} else {
|
|
||||||
throw new RetryLaterException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Log.w("UniversalTransport", "Delivering media message with MMS...");
|
|
||||||
return mmsTransport.deliver(mediaMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isMultipleRecipients(SendReq mediaMessage) {
|
public boolean isMultipleRecipients(SendReq mediaMessage) {
|
||||||
int recipientCount = 0;
|
int recipientCount = 0;
|
||||||
|
|
||||||
|
@ -1,17 +1,29 @@
|
|||||||
package org.thoughtcrime.securesms.transport;
|
package org.thoughtcrime.securesms.transport;
|
||||||
|
|
||||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||||
|
import org.whispersystems.textsecure.push.UnregisteredUserException;
|
||||||
|
|
||||||
public class UntrustedIdentityException extends Exception {
|
public class UntrustedIdentityException extends Exception {
|
||||||
|
|
||||||
private final IdentityKey identityKey;
|
private final IdentityKey identityKey;
|
||||||
|
private final String e164number;
|
||||||
|
|
||||||
public UntrustedIdentityException(String s, IdentityKey identityKey) {
|
public UntrustedIdentityException(String s, String e164number, IdentityKey identityKey) {
|
||||||
super(s);
|
super(s);
|
||||||
|
this.e164number = e164number;
|
||||||
this.identityKey = identityKey;
|
this.identityKey = identityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UntrustedIdentityException(UntrustedIdentityException e) {
|
||||||
|
this(e.getMessage(), e.getE164Number(), e.getIdentityKey());
|
||||||
|
}
|
||||||
|
|
||||||
public IdentityKey getIdentityKey() {
|
public IdentityKey getIdentityKey() {
|
||||||
return identityKey;
|
return identityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getE164Number() {
|
||||||
|
return e164number;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
import org.whispersystems.textsecure.util.Hex;
|
import org.whispersystems.textsecure.util.Hex;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
@ -26,15 +34,29 @@ public class GroupUtil {
|
|||||||
return groupId.startsWith(ENCODED_GROUP_PREFIX);
|
return groupId.startsWith(ENCODED_GROUP_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getActionArgument(GroupContext group) {
|
public static String serializeArguments(byte[] id, String name, List<String> members) {
|
||||||
if (group.getType().equals(GroupContext.Type.CREATE) ||
|
return Base64.encodeBytes(GroupContext.newBuilder()
|
||||||
group.getType().equals(GroupContext.Type.ADD))
|
.setId(ByteString.copyFrom(id))
|
||||||
{
|
.setName(name)
|
||||||
return org.whispersystems.textsecure.util.Util.join(group.getMembersList(), ",");
|
.addAllMembers(members)
|
||||||
} else if (group.getType().equals(GroupContext.Type.MODIFY)) {
|
.build().toByteArray());
|
||||||
return group.getName();
|
}
|
||||||
|
|
||||||
|
public static String serializeArguments(GroupContext context) {
|
||||||
|
return Base64.encodeBytes(context.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getSerializedArgumentMembers(String serialized) {
|
||||||
|
if (serialized == null) {
|
||||||
|
return new LinkedList<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
try {
|
||||||
|
GroupContext context = GroupContext.parseFrom(Base64.decode(serialized));
|
||||||
|
return context.getMembersList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("GroupUtil", e);
|
||||||
|
return new LinkedList<String>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ public class SendReq extends MultimediaMessagePdu {
|
|||||||
private static final String TAG = "SendReq";
|
private static final String TAG = "SendReq";
|
||||||
private long databaseMessageId;
|
private long databaseMessageId;
|
||||||
private long messageBox;
|
private long messageBox;
|
||||||
|
private int groupAction;
|
||||||
|
private String groupActionArguments;
|
||||||
|
|
||||||
public SendReq() {
|
public SendReq() {
|
||||||
super();
|
super();
|
||||||
@ -90,11 +92,15 @@ public class SendReq extends MultimediaMessagePdu {
|
|||||||
super(headers, body);
|
super(headers, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendReq(PduHeaders headers, PduBody body, long messageId, long messageBox) {
|
public SendReq(PduHeaders headers, PduBody body, long messageId, long messageBox,
|
||||||
|
int groupAction, String groupActionArguments)
|
||||||
|
{
|
||||||
super(headers, body);
|
super(headers, body);
|
||||||
this.databaseMessageId = messageId;
|
this.databaseMessageId = messageId;
|
||||||
this.messageBox = messageBox;
|
this.messageBox = messageBox;
|
||||||
}
|
this.groupAction = groupAction;
|
||||||
|
this.groupActionArguments = groupActionArguments;
|
||||||
|
}
|
||||||
|
|
||||||
public long getDatabaseMessageBox() {
|
public long getDatabaseMessageBox() {
|
||||||
return this.messageBox;
|
return this.messageBox;
|
||||||
@ -103,6 +109,22 @@ public class SendReq extends MultimediaMessagePdu {
|
|||||||
public long getDatabaseMessageId() {
|
public long getDatabaseMessageId() {
|
||||||
return databaseMessageId;
|
return databaseMessageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getGroupAction() {
|
||||||
|
return this.groupAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupActionArguments() {
|
||||||
|
return this.groupActionArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupAction(int groupAction) {
|
||||||
|
this.groupAction = groupAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupActionArguments(String groupActionArguments) {
|
||||||
|
this.groupActionArguments = groupActionArguments;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Bcc value.
|
* Get Bcc value.
|
||||||
|
Loading…
Reference in New Issue
Block a user