Update to latest version of libtextsecure for simplified interface.

1) Switch to new TextSecureAddress addressing, rather than mixing
   long-based recipient IDs into libtextsecure.

2) Get rid of RecipientFormattingException throws in calls to
   RecipientFactory.

Closes #2570
This commit is contained in:
Moxie Marlinspike 2015-03-03 11:44:49 -08:00
parent ed5b3f8679
commit 5602a3dfc0
37 changed files with 283 additions and 359 deletions

View file

@ -57,7 +57,7 @@ dependencies {
compile 'org.whispersystems:jobmanager:0.10.0' compile 'org.whispersystems:jobmanager:0.10.0'
compile 'org.whispersystems:libpastelog:1.0.4' compile 'org.whispersystems:libpastelog:1.0.4'
compile 'org.whispersystems:textsecure-android:1.0.0' compile 'org.whispersystems:textsecure-android:1.1.0'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
@ -96,14 +96,14 @@ dependencyVerification {
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a', 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
'org.whispersystems:jobmanager:01f35586c43aa3806f1c18d3d6a5a972def98103ba1a5a9ca3eec08d15f974b7', 'org.whispersystems:jobmanager:01f35586c43aa3806f1c18d3d6a5a972def98103ba1a5a9ca3eec08d15f974b7',
'org.whispersystems:libpastelog:3ccf00fe1597eb8ca1e5de99b17fc225387a1b80b5bbc00ec1bc4d4f3ea9cdde', 'org.whispersystems:libpastelog:3ccf00fe1597eb8ca1e5de99b17fc225387a1b80b5bbc00ec1bc4d4f3ea9cdde',
'org.whispersystems:textsecure-android:af67cb5d8770de6a2ae9c3d64b5ad06b63b24d2d9fb6dc3c4298d7acc60caa2a', 'org.whispersystems:textsecure-android:d2a8630a796222459163effba01456a322f6f15a63668393fb1203f89180a21a',
'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de', 'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
'org.whispersystems:axolotl-android:9cc3328b5a6ca6d407543f3072fd0fbf9080e1ca13ba9d7745f6475d076c2856', 'org.whispersystems:axolotl-android:68e81fcb2d54626925574740263b8c2501e96f557dabedeeba7fa414deccef99',
'org.whispersystems:textsecure-java:bf101047ef41ece9e4dcffdbb0506e185a888c42693d9cb78754d93f01d0c4c7', 'org.whispersystems:textsecure-java:f552a3cfee5ba111057c700cbe5208a4c7fe31488d17ddf49f01dd974026b00d',
'org.whispersystems:axolotl-java:793556b1397da323ad61d5b6a3efaba464972315d4e3f2e771a9183415aeceaf', 'org.whispersystems:axolotl-java:4af04115903cbc5581b32ce82d0518a704dd039f48138d26eb518fa3a2bfee91',
'org.whispersystems:curve25519-android:3c29a4131a69b0d16baaa3d707678deb39602c3a3ffd75805ce7f9db252e5d0d', 'org.whispersystems:curve25519-android:3c29a4131a69b0d16baaa3d707678deb39602c3a3ffd75805ce7f9db252e5d0d',
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab', 'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74', 'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',

View file

@ -26,14 +26,12 @@ import android.widget.Button;
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator; import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.protocol.Tag; import org.thoughtcrime.securesms.protocol.Tag;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.MemoryCleaner; import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
/** /**
* Activity which prompts the user to initiate a secure * Activity which prompts the user to initiate a secure
@ -117,7 +115,6 @@ public class AutoInitiateActivity extends BaseActivity {
MasterSecret masterSecret, MasterSecret masterSecret,
Recipient recipient) Recipient recipient)
{ {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); return SessionUtil.hasSession(context, masterSecret, recipient);
return sessionStore.containsSession(recipient.getRecipientId(), TextSecureAddress.DEFAULT_DEVICE_ID);
} }
} }

View file

@ -61,7 +61,7 @@ import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase; import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft; import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -102,8 +102,6 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -681,14 +679,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void initializeSecurity() { private void initializeSecurity() {
SessionStore sessionStore = new TextSecureSessionStore(this, masterSecret);
Recipient primaryRecipient = getRecipients() == null ? null : getRecipients().getPrimaryRecipient(); Recipient primaryRecipient = getRecipients() == null ? null : getRecipients().getPrimaryRecipient();
boolean isPushDestination = DirectoryHelper.isPushDestination(this, getRecipients()); boolean isPushDestination = DirectoryHelper.isPushDestination(this, getRecipients());
boolean isSecureSmsAllowed = (!isPushDestination || DirectoryHelper.isSmsFallbackAllowed(this, getRecipients())); boolean isSecureSmsAllowed = (!isPushDestination || DirectoryHelper.isSmsFallbackAllowed(this, getRecipients()));
boolean isSecureSmsDestination = isSecureSmsAllowed && boolean isSecureSmsDestination = isSecureSmsAllowed &&
isSingleConversation() && isSingleConversation() &&
sessionStore.containsSession(primaryRecipient.getRecipientId(), SessionUtil.hasSession(this, masterSecret, primaryRecipient);
TextSecureAddress.DEFAULT_DEVICE_ID);
if (isPushDestination || isSecureSmsDestination) { if (isPushDestination || isSecureSmsDestination) {
this.isEncryptedConversation = true; this.isEncryptedConversation = true;

View file

@ -383,17 +383,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
List<ContactData> selected = data.getParcelableArrayListExtra("contacts"); List<ContactData> selected = data.getParcelableArrayListExtra("contacts");
for (ContactData contact : selected) { for (ContactData contact : selected) {
for (ContactAccessor.NumberData numberData : contact.numbers) { for (ContactAccessor.NumberData numberData : contact.numbers) {
try { Recipient recipient = RecipientFactory.getRecipientsFromString(this, numberData.number, false)
Recipient recipient = RecipientFactory.getRecipientsFromString(this, numberData.number, false) .getPrimaryRecipient();
.getPrimaryRecipient();
if (!selectedContacts.contains(recipient) && if (!selectedContacts.contains(recipient) &&
(existingContacts == null || !existingContacts.contains(recipient)) && (existingContacts == null || !existingContacts.contains(recipient)) &&
recipient != null) { recipient != null) {
addSelectedContact(recipient); addSelectedContact(recipient);
}
} catch (RecipientFormattingException e) {
Log.w(TAG, e);
} }
} }
} }
@ -455,25 +451,20 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
Set<String> e164numbers) Set<String> e164numbers)
throws InvalidNumberException throws InvalidNumberException
{ {
String groupRecipientId = GroupUtil.getEncodedId(groupId);
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
try { GroupContext context = GroupContext.newBuilder()
String groupRecipientId = GroupUtil.getEncodedId(groupId); .setId(ByteString.copyFrom(groupId))
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false); .setType(GroupContext.Type.UPDATE)
.setName(groupName)
.addAllMembers(e164numbers)
.build();
GroupContext context = GroupContext.newBuilder() OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar);
.setId(ByteString.copyFrom(groupId)) long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1, false);
.setType(GroupContext.Type.UPDATE)
.setName(groupName)
.addAllMembers(e164numbers)
.build();
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar); return new Pair<>(threadId, groupRecipient);
long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1, false);
return new Pair<>(threadId, groupRecipient);
} catch (RecipientFormattingException e) {
throw new AssertionError(e);
}
} }
private long handleCreateMmsGroup(Set<Recipient> members) { private long handleCreateMmsGroup(Set<Recipient> members) {

View file

@ -99,15 +99,11 @@ public class ImageMediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, record.getDate()); intent.putExtra(MediaPreviewActivity.DATE_EXTRA, record.getDate());
if (!TextUtils.isEmpty(record.getAddress())) { if (!TextUtils.isEmpty(record.getAddress())) {
try { Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(),
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), record.getAddress(),
record.getAddress(), true);
true); if (recipients != null && recipients.getPrimaryRecipient() != null) {
if (recipients != null && recipients.getPrimaryRecipient() != null) { intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
}
} catch (RecipientFormattingException rfe) {
Log.w(TAG, rfe);
} }
} }
intent.setDataAndType(record.getUri(), record.getContentType()); intent.setDataAndType(record.getUri(), record.getContentType());

View file

@ -136,14 +136,10 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity
Recipients recipients = new Recipients(new LinkedList<Recipient>()); Recipients recipients = new Recipients(new LinkedList<Recipient>());
for (ContactAccessor.NumberData numberData : contactData.numbers) { for (ContactAccessor.NumberData numberData : contactData.numbers) {
if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) { if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) {
try { Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(NewConversationActivity.this,
Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(NewConversationActivity.this, numberData.number,
numberData.number, false);
false); recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList());
recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList());
} catch (RecipientFormattingException rfe) {
Log.w(TAG, "Caught RecipientFormattingException when trying to convert a selected number to a Recipient.", rfe);
}
} }
} }
return recipients; return recipients;

View file

@ -136,10 +136,9 @@ public class ReceiveKeyActivity extends BaseActivity {
} }
private boolean isTrusted(IdentityKey identityKey) { private boolean isTrusted(IdentityKey identityKey) {
long recipientId = recipient.getRecipientId();
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(this, masterSecret); IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(this, masterSecret);
return identityKeyStore.isTrustedIdentity(recipientId, identityKey); return identityKeyStore.isTrustedIdentity(recipient.getNumber(), identityKey);
} }
private void initializeKey() private void initializeKey()

View file

@ -198,12 +198,12 @@ public class RoutingActivity extends PassphraseRequiredActionBarActivity {
Recipients recipients; Recipients recipients;
String body = getIntent().getStringExtra("sms_body"); String body = getIntent().getStringExtra("sms_body");
long threadId = getIntent().getLongExtra("thread_id", -1); long threadId = getIntent().getLongExtra("thread_id", -1);
Uri data = getIntent().getData();
try { if (data != null && data.getSchemeSpecificPart() != null) {
String data = getIntent().getData().getSchemeSpecificPart(); recipients = RecipientFactory.getRecipientsFromString(this, data.getSchemeSpecificPart(), false);
recipients = RecipientFactory.getRecipientsFromString(this, data, false);
threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
} catch (RecipientFormattingException rfe) { } else {
recipients = null; recipients = null;
} }

View file

@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MemoryCleaner; import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
@ -183,9 +184,9 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
} }
private IdentityKey getRemoteIdentityKey(MasterSecret masterSecret, Recipient recipient) { private IdentityKey getRemoteIdentityKey(MasterSecret masterSecret, Recipient recipient) {
SessionStore sessionStore = new TextSecureSessionStore(this, masterSecret); SessionStore sessionStore = new TextSecureSessionStore(this, masterSecret);
SessionRecord record = sessionStore.loadSession(recipient.getRecipientId(), AxolotlAddress axolotlAddress = new AxolotlAddress(recipient.getNumber(), TextSecureAddress.DEFAULT_DEVICE_ID);
TextSecureAddress.DEFAULT_DEVICE_ID); SessionRecord record = sessionStore.loadSession(axolotlAddress);
if (record == null) { if (record == null) {
return null; return null;

View file

@ -128,13 +128,7 @@ public class RecipientsEditor extends MultiAutoCompleteTextView {
} }
public Recipients constructContactsFromInput() { public Recipients constructContactsFromInput() {
Recipients r = null; return RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false);
try {
r = RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false);
} catch (RecipientFormattingException e) {
Log.w( "RecipientsEditor", e);
}
return r;
} }
private boolean isValidAddress(String number, boolean isMms) { private boolean isValidAddress(String number, boolean isMms) {

View file

@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage; import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.Dialogs;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.SessionBuilder; import org.whispersystems.libaxolotl.SessionBuilder;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.state.IdentityKeyStore; import org.whispersystems.libaxolotl.state.IdentityKeyStore;
@ -67,8 +68,8 @@ public class KeyExchangeInitiator {
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret); IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret);
SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityKeyStore, recipient.getRecipientId(), identityKeyStore, new AxolotlAddress(recipient.getNumber(),
TextSecureAddress.DEFAULT_DEVICE_ID); TextSecureAddress.DEFAULT_DEVICE_ID));
KeyExchangeMessage keyExchangeMessage = sessionBuilder.process(); KeyExchangeMessage keyExchangeMessage = sessionBuilder.process();
String serializedMessage = Base64.encodeBytesWithoutPadding(keyExchangeMessage.serialize()); String serializedMessage = Base64.encodeBytesWithoutPadding(keyExchangeMessage.serialize());
@ -81,7 +82,7 @@ public class KeyExchangeInitiator {
Recipient recipient) Recipient recipient)
{ {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
SessionRecord sessionRecord = sessionStore.loadSession(recipient.getRecipientId(), TextSecureAddress.DEFAULT_DEVICE_ID); SessionRecord sessionRecord = sessionStore.loadSession(new AxolotlAddress(recipient.getNumber(), TextSecureAddress.DEFAULT_DEVICE_ID));
return sessionRecord.getSessionState().hasPendingKeyExchange(); return sessionRecord.getSessionState().hasPendingKeyExchange();
} }

View file

@ -5,11 +5,10 @@ import android.util.Log;
import org.thoughtcrime.securesms.mms.TextTransport; import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.protocol.WirePrefix;
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.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.DuplicateMessageException; import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
@ -49,9 +48,7 @@ public class MmsCipher {
NoSessionException NoSessionException
{ {
try { try {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, pdu.getFrom().getString(), false); SessionCipher sessionCipher = new SessionCipher(axolotlStore, new AxolotlAddress(pdu.getFrom().getString(), TextSecureAddress.DEFAULT_DEVICE_ID));
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
SessionCipher sessionCipher = new SessionCipher(axolotlStore, recipientId, 1);
Optional<byte[]> ciphertext = getEncryptedData(pdu); Optional<byte[]> ciphertext = getEncryptedData(pdu);
if (!ciphertext.isPresent()) { if (!ciphertext.isPresent()) {
@ -83,7 +80,7 @@ public class MmsCipher {
MultimediaMessagePdu plaintextGenericPdu = (MultimediaMessagePdu) new PduParser(plaintext).parse(); MultimediaMessagePdu plaintextGenericPdu = (MultimediaMessagePdu) new PduParser(plaintext).parse();
return new RetrieveConf(plaintextGenericPdu.getPduHeaders(), plaintextGenericPdu.getBody()); return new RetrieveConf(plaintextGenericPdu.getPduHeaders(), plaintextGenericPdu.getBody());
} catch (RecipientFormattingException | IOException e) { } catch (IOException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
} }
@ -93,19 +90,17 @@ public class MmsCipher {
{ {
EncodedStringValue[] encodedRecipient = message.getTo(); EncodedStringValue[] encodedRecipient = message.getTo();
String recipientString = encodedRecipient[0].getString(); String recipientString = encodedRecipient[0].getString();
Recipients recipients = RecipientFactory.getRecipientsFromString(context, recipientString, false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
byte[] pduBytes = new PduComposer(context, message).make(); byte[] pduBytes = new PduComposer(context, message).make();
if (pduBytes == null) { if (pduBytes == null) {
throw new UndeliverableMessageException("PDU composition failed, null payload"); throw new UndeliverableMessageException("PDU composition failed, null payload");
} }
if (!axolotlStore.containsSession(recipientId, TextSecureAddress.DEFAULT_DEVICE_ID)) { if (!axolotlStore.containsSession(new AxolotlAddress(recipientString, TextSecureAddress.DEFAULT_DEVICE_ID))) {
throw new NoSessionException("No session for: " + recipientId); throw new NoSessionException("No session for: " + recipientString);
} }
SessionCipher cipher = new SessionCipher(axolotlStore, recipientId, TextSecureAddress.DEFAULT_DEVICE_ID); SessionCipher cipher = new SessionCipher(axolotlStore, new AxolotlAddress(recipientString, TextSecureAddress.DEFAULT_DEVICE_ID));
CiphertextMessage ciphertextMessage = cipher.encrypt(pduBytes); CiphertextMessage ciphertextMessage = cipher.encrypt(pduBytes);
byte[] encryptedPduBytes = textTransport.getEncodedMessage(ciphertextMessage.serialize()); byte[] encryptedPduBytes = textTransport.getEncodedMessage(ciphertextMessage.serialize());

View file

@ -0,0 +1,24 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
public class SessionUtil {
public static boolean hasSession(Context context, MasterSecret masterSecret, Recipient recipient) {
return hasSession(context, masterSecret, recipient.getNumber());
}
public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull String number) {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
AxolotlAddress axolotlAddress = new AxolotlAddress(number, TextSecureAddress.DEFAULT_DEVICE_ID);
return sessionStore.containsSession(axolotlAddress);
}
}

View file

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage; import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.DuplicateMessageException; import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.InvalidKeyIdException;
@ -49,20 +50,18 @@ public class SmsCipher {
DuplicateMessageException, NoSessionException DuplicateMessageException, NoSessionException
{ {
try { try {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes()); byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
WhisperMessage whisperMessage = new WhisperMessage(decoded); WhisperMessage whisperMessage = new WhisperMessage(decoded);
SessionCipher sessionCipher = new SessionCipher(axolotlStore, recipientId, 1); SessionCipher sessionCipher = new SessionCipher(axolotlStore, new AxolotlAddress(message.getSender(), TextSecureAddress.DEFAULT_DEVICE_ID));
byte[] padded = sessionCipher.decrypt(whisperMessage); byte[] padded = sessionCipher.decrypt(whisperMessage);
byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded); byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded);
if (message.isEndSession() && "TERMINATE".equals(new String(plaintext))) { if (message.isEndSession() && "TERMINATE".equals(new String(plaintext))) {
axolotlStore.deleteSession(recipientId, 1); axolotlStore.deleteSession(new AxolotlAddress(message.getSender(), TextSecureAddress.DEFAULT_DEVICE_ID));
} }
return message.withMessageBody(new String(plaintext)); return message.withMessageBody(new String(plaintext));
} catch (RecipientFormattingException | IOException e) { } catch (IOException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
} }
@ -72,28 +71,27 @@ public class SmsCipher {
UntrustedIdentityException, LegacyMessageException UntrustedIdentityException, LegacyMessageException
{ {
try { try {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes()); byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyMessage = new PreKeyWhisperMessage(decoded); PreKeyWhisperMessage preKeyMessage = new PreKeyWhisperMessage(decoded);
SessionCipher sessionCipher = new SessionCipher(axolotlStore, recipients.getPrimaryRecipient().getRecipientId(), 1); SessionCipher sessionCipher = new SessionCipher(axolotlStore, new AxolotlAddress(message.getSender(), TextSecureAddress.DEFAULT_DEVICE_ID));
byte[] padded = sessionCipher.decrypt(preKeyMessage); byte[] padded = sessionCipher.decrypt(preKeyMessage);
byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded); byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded);
return new IncomingEncryptedMessage(message, new String(plaintext)); return new IncomingEncryptedMessage(message, new String(plaintext));
} catch (RecipientFormattingException | IOException | InvalidKeyException | InvalidKeyIdException e) { } catch (IOException | InvalidKeyException | InvalidKeyIdException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
} }
public OutgoingTextMessage encrypt(OutgoingTextMessage message) throws NoSessionException { public OutgoingTextMessage encrypt(OutgoingTextMessage message) throws NoSessionException {
byte[] paddedBody = transportDetails.getPaddedMessageBody(message.getMessageBody().getBytes()); byte[] paddedBody = transportDetails.getPaddedMessageBody(message.getMessageBody().getBytes());
long recipientId = message.getRecipients().getPrimaryRecipient().getRecipientId(); String recipientNumber = message.getRecipients().getPrimaryRecipient().getNumber();
if (!axolotlStore.containsSession(recipientId, TextSecureAddress.DEFAULT_DEVICE_ID)) { if (!axolotlStore.containsSession(new AxolotlAddress(recipientNumber, TextSecureAddress.DEFAULT_DEVICE_ID))) {
throw new NoSessionException("No session for: " + recipientId); throw new NoSessionException("No session for: " + recipientNumber);
} }
SessionCipher cipher = new SessionCipher(axolotlStore, recipientId, TextSecureAddress.DEFAULT_DEVICE_ID); SessionCipher cipher = new SessionCipher(axolotlStore, new AxolotlAddress(recipientNumber, TextSecureAddress.DEFAULT_DEVICE_ID));
CiphertextMessage ciphertextMessage = cipher.encrypt(paddedBody); CiphertextMessage ciphertextMessage = cipher.encrypt(paddedBody);
String encodedCiphertext = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize())); String encodedCiphertext = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize()));
@ -110,8 +108,9 @@ public class SmsCipher {
{ {
try { try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient(); Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
AxolotlAddress axolotlAddress = new AxolotlAddress(message.getSender(), TextSecureAddress.DEFAULT_DEVICE_ID);
KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(transportDetails.getDecodedMessage(message.getMessageBody().getBytes())); KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(transportDetails.getDecodedMessage(message.getMessageBody().getBytes()));
SessionBuilder sessionBuilder = new SessionBuilder(axolotlStore, recipient.getRecipientId(), 1); SessionBuilder sessionBuilder = new SessionBuilder(axolotlStore, axolotlAddress);
KeyExchangeMessage response = sessionBuilder.process(exchangeMessage); KeyExchangeMessage response = sessionBuilder.process(exchangeMessage);
@ -121,7 +120,7 @@ public class SmsCipher {
} else { } else {
return null; return null;
} }
} catch (RecipientFormattingException | IOException | InvalidKeyException e) { } catch (IOException | InvalidKeyException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
} }

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context; import android.content.Context;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.InvalidKeyIdException;
@ -42,13 +43,13 @@ public class TextSecureAxolotlStore implements AxolotlStore {
} }
@Override @Override
public void saveIdentity(long recipientId, IdentityKey identityKey) { public void saveIdentity(String number, IdentityKey identityKey) {
identityKeyStore.saveIdentity(recipientId, identityKey); identityKeyStore.saveIdentity(number, identityKey);
} }
@Override @Override
public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey) { public boolean isTrustedIdentity(String number, IdentityKey identityKey) {
return identityKeyStore.isTrustedIdentity(recipientId, identityKey); return identityKeyStore.isTrustedIdentity(number, identityKey);
} }
@Override @Override
@ -72,33 +73,33 @@ public class TextSecureAxolotlStore implements AxolotlStore {
} }
@Override @Override
public SessionRecord loadSession(long recipientId, int deviceId) { public SessionRecord loadSession(AxolotlAddress axolotlAddress) {
return sessionStore.loadSession(recipientId, deviceId); return sessionStore.loadSession(axolotlAddress);
} }
@Override @Override
public List<Integer> getSubDeviceSessions(long recipientId) { public List<Integer> getSubDeviceSessions(String number) {
return sessionStore.getSubDeviceSessions(recipientId); return sessionStore.getSubDeviceSessions(number);
} }
@Override @Override
public void storeSession(long recipientId, int deviceId, SessionRecord record) { public void storeSession(AxolotlAddress axolotlAddress, SessionRecord record) {
sessionStore.storeSession(recipientId, deviceId, record); sessionStore.storeSession(axolotlAddress, record);
} }
@Override @Override
public boolean containsSession(long recipientId, int deviceId) { public boolean containsSession(AxolotlAddress axolotlAddress) {
return sessionStore.containsSession(recipientId, deviceId); return sessionStore.containsSession(axolotlAddress);
} }
@Override @Override
public void deleteSession(long recipientId, int deviceId) { public void deleteSession(AxolotlAddress axolotlAddress) {
sessionStore.deleteSession(recipientId, deviceId); sessionStore.deleteSession(axolotlAddress);
} }
@Override @Override
public void deleteAllSessions(long recipientId) { public void deleteAllSessions(String number) {
sessionStore.deleteAllSessions(recipientId); sessionStore.deleteAllSessions(number);
} }
@Override @Override

View file

@ -5,6 +5,7 @@ import android.content.Context;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.IdentityKeyPair;
@ -31,12 +32,14 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
} }
@Override @Override
public void saveIdentity(long recipientId, IdentityKey identityKey) { public void saveIdentity(String name, IdentityKey identityKey) {
long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey); DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey);
} }
@Override @Override
public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey) { public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
return DatabaseFactory.getIdentityDatabase(context) return DatabaseFactory.getIdentityDatabase(context)
.isValidIdentity(masterSecret, recipientId, identityKey); .isValidIdentity(masterSecret, recipientId, identityKey);
} }

View file

@ -5,12 +5,15 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.Conversions;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionState; import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.api.push.TextSecureAddress; import org.whispersystems.textsecure.api.push.TextSecureAddress;
import org.thoughtcrime.securesms.util.Conversions;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -42,11 +45,11 @@ public class TextSecureSessionStore implements SessionStore {
} }
@Override @Override
public SessionRecord loadSession(long recipientId, int deviceId) { public SessionRecord loadSession(AxolotlAddress address) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
try { try {
MasterCipher cipher = new MasterCipher(masterSecret); MasterCipher cipher = new MasterCipher(masterSecret);
FileInputStream in = new FileInputStream(getSessionFile(recipientId, deviceId)); FileInputStream in = new FileInputStream(getSessionFile(address));
int versionMarker = readInteger(in); int versionMarker = readInteger(in);
@ -74,11 +77,11 @@ public class TextSecureSessionStore implements SessionStore {
} }
@Override @Override
public void storeSession(long recipientId, int deviceId, SessionRecord record) { public void storeSession(AxolotlAddress address, SessionRecord record) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
try { try {
MasterCipher masterCipher = new MasterCipher(masterSecret); MasterCipher masterCipher = new MasterCipher(masterSecret);
RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(recipientId, deviceId), "rw"); RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(address), "rw");
FileChannel out = sessionFile.getChannel(); FileChannel out = sessionFile.getChannel();
out.position(0); out.position(0);
@ -94,32 +97,33 @@ public class TextSecureSessionStore implements SessionStore {
} }
@Override @Override
public boolean containsSession(long recipientId, int deviceId) { public boolean containsSession(AxolotlAddress address) {
return getSessionFile(recipientId, deviceId).exists() && return getSessionFile(address).exists() &&
loadSession(recipientId, deviceId).getSessionState().hasSenderChain(); loadSession(address).getSessionState().hasSenderChain();
} }
@Override @Override
public void deleteSession(long recipientId, int deviceId) { public void deleteSession(AxolotlAddress address) {
getSessionFile(recipientId, deviceId).delete(); getSessionFile(address).delete();
} }
@Override @Override
public void deleteAllSessions(long recipientId) { public void deleteAllSessions(String name) {
List<Integer> devices = getSubDeviceSessions(recipientId); List<Integer> devices = getSubDeviceSessions(name);
deleteSession(recipientId, TextSecureAddress.DEFAULT_DEVICE_ID); deleteSession(new AxolotlAddress(name, TextSecureAddress.DEFAULT_DEVICE_ID));
for (int device : devices) { for (int device : devices) {
deleteSession(recipientId, device); deleteSession(new AxolotlAddress(name, device));
} }
} }
@Override @Override
public List<Integer> getSubDeviceSessions(long recipientId) { public List<Integer> getSubDeviceSessions(String name) {
List<Integer> results = new LinkedList<>(); long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
File parent = getSessionDirectory(); List<Integer> results = new LinkedList<>();
String[] children = parent.list(); File parent = getSessionDirectory();
String[] children = parent.list();
if (children == null) return results; if (children == null) return results;
@ -132,15 +136,15 @@ public class TextSecureSessionStore implements SessionStore {
results.add(Integer.parseInt(parts[1])); results.add(Integer.parseInt(parts[1]));
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w("SessionRecordV2", e); Log.w(TAG, e);
} }
} }
return results; return results;
} }
private File getSessionFile(long recipientId, int deviceId) { private File getSessionFile(AxolotlAddress address) {
return new File(getSessionDirectory(), getSessionName(recipientId, deviceId)); return new File(getSessionDirectory(), getSessionName(address));
} }
private File getSessionDirectory() { private File getSessionDirectory() {
@ -155,7 +159,12 @@ public class TextSecureSessionStore implements SessionStore {
return directory; return directory;
} }
private String getSessionName(long recipientId, int deviceId) { private String getSessionName(AxolotlAddress axolotlAddress) {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, axolotlAddress.getName(), true)
.getPrimaryRecipient();
long recipientId = recipient.getRecipientId();
int deviceId = axolotlAddress.getDeviceId();
return recipientId + (deviceId == TextSecureAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId); return recipientId + (deviceId == TextSecureAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
} }

View file

@ -96,12 +96,8 @@ public class GroupDatabase extends Database {
if (!includeSelf && member.equals(localNumber)) if (!includeSelf && member.equals(localNumber))
continue; continue;
try { recipients.addAll(RecipientFactory.getRecipientsFromString(context, member, false)
recipients.addAll(RecipientFactory.getRecipientsFromString(context, member, false) .getRecipientsList());
.getRecipientsList());
} catch (RecipientFormattingException e) {
Log.w("GroupDatabase", e);
}
} }
return new Recipients(recipients); return new Recipients(recipients);

View file

@ -140,12 +140,8 @@ public class MmsAddressDatabase extends Database {
for (String number : numbers) { for (String number : numbers) {
if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(number)) { if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(number)) {
try { results.add(RecipientFactory.getRecipientsFromString(context, number, false)
results.add(RecipientFactory.getRecipientsFromString(context, number, false) .getPrimaryRecipient());
.getPrimaryRecipient());
} catch (RecipientFormattingException e) {
Log.w(TAG, e);
}
} }
} }

View file

@ -1062,22 +1062,17 @@ public class MmsDatabase extends MessagingDatabase {
} }
private Recipients getRecipientsFor(String address) { private Recipients getRecipientsFor(String address) {
try { if (TextUtils.isEmpty(address) || address.equals("insert-address-token")) {
if (TextUtils.isEmpty(address) || address.equals("insert-address-token")) {
return new Recipients(Recipient.getUnknownRecipient(context));
}
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false);
if (recipients == null || recipients.isEmpty()) {
return new Recipients(Recipient.getUnknownRecipient(context));
}
return recipients;
} catch (RecipientFormattingException e) {
Log.w("MmsDatabase", e);
return new Recipients(Recipient.getUnknownRecipient(context)); return new Recipients(Recipient.getUnknownRecipient(context));
} }
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false);
if (recipients == null || recipients.isEmpty()) {
return new Recipients(Recipient.getUnknownRecipient(context));
}
return recipients;
} }
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) { private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {

View file

@ -55,35 +55,31 @@ public class PlaintextBackupImporter {
XmlBackup.XmlBackupItem item; XmlBackup.XmlBackupItem item;
while ((item = backup.getNext()) != null) { while ((item = backup.getNext()) != null) {
try { Recipients recipients = RecipientFactory.getRecipientsFromString(context, item.getAddress(), false);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, item.getAddress(), false); long threadId = threads.getThreadIdFor(recipients);
long threadId = threads.getThreadIdFor(recipients); SQLiteStatement statement = db.createInsertStatement(transaction);
SQLiteStatement statement = db.createInsertStatement(transaction);
if (item.getAddress() == null || item.getAddress().equals("null")) if (item.getAddress() == null || item.getAddress().equals("null"))
continue; continue;
if (!isAppropriateTypeForImport(item.getType())) if (!isAppropriateTypeForImport(item.getType()))
continue; continue;
addStringToStatement(statement, 1, item.getAddress()); addStringToStatement(statement, 1, item.getAddress());
addNullToStatement(statement, 2); addNullToStatement(statement, 2);
addLongToStatement(statement, 3, item.getDate()); addLongToStatement(statement, 3, item.getDate());
addLongToStatement(statement, 4, item.getDate()); addLongToStatement(statement, 4, item.getDate());
addLongToStatement(statement, 5, item.getProtocol()); addLongToStatement(statement, 5, item.getProtocol());
addLongToStatement(statement, 6, item.getRead()); addLongToStatement(statement, 6, item.getRead());
addLongToStatement(statement, 7, item.getStatus()); addLongToStatement(statement, 7, item.getStatus());
addTranslatedTypeToStatement(statement, 8, item.getType()); addTranslatedTypeToStatement(statement, 8, item.getType());
addNullToStatement(statement, 9); addNullToStatement(statement, 9);
addStringToStatement(statement, 10, item.getSubject()); addStringToStatement(statement, 10, item.getSubject());
addEncryptedStingToStatement(masterCipher, statement, 11, item.getBody()); addEncryptedStingToStatement(masterCipher, statement, 11, item.getBody());
addStringToStatement(statement, 12, item.getServiceCenter()); addStringToStatement(statement, 12, item.getServiceCenter());
addLongToStatement(statement, 13, threadId); addLongToStatement(statement, 13, threadId);
modifiedThreads.add(threadId); modifiedThreads.add(threadId);
statement.execute(); statement.execute();
} catch (RecipientFormattingException rfe) {
Log.w("PlaintextBackupImporter", rfe);
}
} }
for (long threadId : modifiedThreads) { for (long threadId : modifiedThreads) {

View file

@ -390,24 +390,19 @@ public class SmsDatabase extends MessagingDatabase {
Recipients recipients; Recipients recipients;
try { if (message.getSender() != null) {
recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), true); recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), true);
} catch (RecipientFormattingException e) { } else {
Log.w("SmsDatabase", e); Log.w(TAG, "Sender is null, returning unknown recipient");
recipients = new Recipients(Recipient.getUnknownRecipient(context)); recipients = new Recipients(Recipient.getUnknownRecipient(context));
} }
Recipients groupRecipients; Recipients groupRecipients;
try { if (message.getGroupId() == null) {
if (message.getGroupId() == null) {
groupRecipients = null;
} else {
groupRecipients = RecipientFactory.getRecipientsFromString(context, message.getGroupId(), true);
}
} catch (RecipientFormattingException e) {
Log.w("SmsDatabase", e);
groupRecipients = null; groupRecipients = null;
} else {
groupRecipients = RecipientFactory.getRecipientsFromString(context, message.getGroupId(), true);
} }
boolean unread = org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) || boolean unread = org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
@ -643,7 +638,7 @@ public class SmsDatabase extends MessagingDatabase {
} }
private Recipients getRecipientsFor(String address) { private Recipients getRecipientsFor(String address) {
try { if (address != null) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false); Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false);
if (recipients == null || recipients.isEmpty()) { if (recipients == null || recipients.isEmpty()) {
@ -651,8 +646,8 @@ public class SmsDatabase extends MessagingDatabase {
} }
return recipients; return recipients;
} catch (RecipientFormattingException e) { } else {
Log.w("EncryptingSmsDatabase", e); Log.w(TAG, "getRecipientsFor() address is null");
return new Recipients(Recipient.getUnknownRecipient(context)); return new Recipients(Recipient.getUnknownRecipient(context));
} }
} }

View file

@ -150,13 +150,8 @@ public class SmsMigrator {
sb.append(address); sb.append(address);
} }
try { if (sb.length() == 0) return null;
if (sb.length() == 0) return null; else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true);
else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true);
} catch (RecipientFormattingException rfe) {
Log.w("SmsMigrator", rfe);
return null;
}
} }
private static String encrypt(MasterSecret masterSecret, String body) private static String encrypt(MasterSecret masterSecret, String body)

View file

@ -57,21 +57,13 @@ public class TextSecureCommunicationModule {
return new TextSecureMessageSenderFactory() { return new TextSecureMessageSenderFactory() {
@Override @Override
public TextSecureMessageSender create(MasterSecret masterSecret) { public TextSecureMessageSender create(MasterSecret masterSecret) {
try { return new TextSecureMessageSender(Release.PUSH_URL,
String localNumber = TextSecurePreferences.getLocalNumber(context); new TextSecurePushTrustStore(context),
Recipient localRecipient = RecipientFactory.getRecipientsFromString(context, localNumber, false).getPrimaryRecipient(); TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context),
return new TextSecureMessageSender(Release.PUSH_URL, new TextSecureAxolotlStore(context, masterSecret),
new TextSecurePushTrustStore(context), Optional.of((TextSecureMessageSender.EventListener)
TextSecurePreferences.getLocalNumber(context), new SecurityEventListener(context)));
TextSecurePreferences.getPushServerPassword(context),
localRecipient.getRecipientId(),
new TextSecureAxolotlStore(context, masterSecret),
Optional.of((TextSecureMessageSender.EventListener)
new SecurityEventListener(context)));
} catch (RecipientFormattingException e) {
throw new AssertionError(e);
}
} }
}; };
} }

View file

@ -72,13 +72,9 @@ public class AvatarDownloadJob extends MasterSecretJob {
database.updateAvatar(groupId, avatar); database.updateAvatar(groupId, avatar);
try { Recipient groupRecipient = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(groupId), true)
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(groupId), true) .getPrimaryRecipient();
.getPrimaryRecipient(); groupRecipient.setContactPhoto(avatar);
groupRecipient.setContactPhoto(avatar);
} catch (RecipientFormattingException e) {
Log.w("AvatarDownloader", e);
}
} }
} catch (InvalidMessageException | BitmapDecodingException | NonSuccessfulResponseCodeException e) { } catch (InvalidMessageException | BitmapDecodingException | NonSuccessfulResponseCodeException e) {
Log.w(TAG, e); Log.w(TAG, e);

View file

@ -3,9 +3,11 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.TextSecureMessageSender; import org.whispersystems.textsecure.api.TextSecureMessageSender;
import org.whispersystems.textsecure.api.push.TextSecureAddress; import org.whispersystems.textsecure.api.push.TextSecureAddress;
import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException;
@ -46,7 +48,7 @@ public class DeliveryReceiptJob extends ContextJob implements InjectableType {
public void onRun() throws IOException { public void onRun() throws IOException {
Log.w("DeliveryReceiptJob", "Sending delivery receipt..."); Log.w("DeliveryReceiptJob", "Sending delivery receipt...");
TextSecureMessageSender messageSender = messageSenderFactory.create(null); TextSecureMessageSender messageSender = messageSenderFactory.create(null);
TextSecureAddress textSecureAddress = new TextSecureAddress(-1, destination, relay); TextSecureAddress textSecureAddress = new TextSecureAddress(destination, Optional.fromNullable(relay));
messageSender.sendDeliveryReceipt(textSecureAddress, timestamp); messageSender.sendDeliveryReceipt(textSecureAddress, timestamp);
} }

View file

@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.DuplicateMessageException; import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
@ -98,15 +99,13 @@ public class PushDecryptJob extends MasterSecretJob {
private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, long smsMessageId) { private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, long smsMessageId) {
try { try {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
int deviceId = envelope.getSourceDevice(); int deviceId = envelope.getSourceDevice();
AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret); AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret);
TextSecureCipher cipher = new TextSecureCipher(axolotlStore, recipientId, deviceId); TextSecureCipher cipher = new TextSecureCipher(axolotlStore, new AxolotlAddress(envelope.getSource(), deviceId));
TextSecureMessage message = cipher.decrypt(envelope); TextSecureMessage message = cipher.decrypt(envelope);
if (message.isEndSession()) handleEndSessionMessage(masterSecret, recipientId, envelope, message, smsMessageId); if (message.isEndSession()) handleEndSessionMessage(masterSecret, envelope, message, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message, smsMessageId); else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message, smsMessageId);
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId); else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId);
else handleTextMessage(masterSecret, envelope, message, smsMessageId); else handleTextMessage(masterSecret, envelope, message, smsMessageId);
@ -117,7 +116,7 @@ public class PushDecryptJob extends MasterSecretJob {
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleInvalidVersionMessage(masterSecret, envelope, smsMessageId); handleInvalidVersionMessage(masterSecret, envelope, smsMessageId);
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException | RecipientFormattingException e) { } catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleCorruptMessage(masterSecret, envelope, smsMessageId); handleCorruptMessage(masterSecret, envelope, smsMessageId);
} catch (NoSessionException e) { } catch (NoSessionException e) {
@ -135,9 +134,8 @@ public class PushDecryptJob extends MasterSecretJob {
} }
} }
private void handleEndSessionMessage(MasterSecret masterSecret, long recipientId, private void handleEndSessionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope,
TextSecureEnvelope envelope, TextSecureMessage message, TextSecureMessage message, long smsMessageId)
long smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(), IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(),
@ -157,7 +155,7 @@ public class PushDecryptJob extends MasterSecretJob {
} }
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
sessionStore.deleteAllSessions(recipientId); sessionStore.deleteAllSessions(envelope.getSource());
SecurityEvent.broadcastSecurityUpdateEvent(context, threadId); SecurityEvent.broadcastSecurityUpdateEvent(context, threadId);
MessageNotifier.updateNotification(context, masterSecret, threadId); MessageNotifier.updateNotification(context, masterSecret, threadId);
@ -309,7 +307,7 @@ public class PushDecryptJob extends MasterSecretJob {
database.markAsPreKeyBundle(smsMessageId); database.markAsPreKeyBundle(smsMessageId);
database.addMismatchedIdentity(smsMessageId, recipientId, identityKey); database.addMismatchedIdentity(smsMessageId, recipientId, identityKey);
} }
} catch (RecipientFormattingException | InvalidMessageException | InvalidVersionException e) { } catch (InvalidMessageException | InvalidVersionException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }

View file

@ -72,7 +72,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override @Override
public void onSend(MasterSecret masterSecret) public void onSend(MasterSecret masterSecret)
throws MmsException, IOException, NoSuchMessageException, RecipientFormattingException throws MmsException, IOException, NoSuchMessageException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
SendReq message = database.getOutgoingMessage(masterSecret, messageId); SendReq message = database.getOutgoingMessage(masterSecret, messageId);
@ -164,7 +164,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List<TextSecureAddress> addresses = new LinkedList<>(); List<TextSecureAddress> addresses = new LinkedList<>();
for (Recipient recipient : recipients.getRecipientsList()) { for (Recipient recipient : recipients.getRecipientsList()) {
addresses.add(getPushAddress(recipient)); addresses.add(getPushAddress(recipient.getNumber()));
} }
return addresses; return addresses;
@ -172,7 +172,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
private List<TextSecureAddress> getPushAddresses(long filterRecipientId) throws InvalidNumberException { private List<TextSecureAddress> getPushAddresses(long filterRecipientId) throws InvalidNumberException {
List<TextSecureAddress> addresses = new LinkedList<>(); List<TextSecureAddress> addresses = new LinkedList<>();
addresses.add(getPushAddress(RecipientFactory.getRecipientForId(context, filterRecipientId, false))); addresses.add(getPushAddress(RecipientFactory.getRecipientForId(context, filterRecipientId, false).getNumber()));
return addresses; return addresses;
} }

View file

@ -5,7 +5,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -14,13 +14,11 @@ import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.PartParser; import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.recipients.Recipient; 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.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.whispersystems.libaxolotl.state.AxolotlStore;
import org.whispersystems.textsecure.api.TextSecureMessageSender; import org.whispersystems.textsecure.api.TextSecureMessageSender;
import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
@ -62,7 +60,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
@Override @Override
public void onSend(MasterSecret masterSecret) public void onSend(MasterSecret masterSecret)
throws RetryLaterException, MmsException, NoSuchMessageException, throws RetryLaterException, MmsException, NoSuchMessageException,
UndeliverableMessageException, RecipientFormattingException UndeliverableMessageException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
SendReq message = database.getOutgoingMessage(masterSecret, messageId); SendReq message = database.getOutgoingMessage(masterSecret, messageId);
@ -117,8 +115,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try { try {
prepareMessageMedia(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false); prepareMessageMedia(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, destination, false); TextSecureAddress address = getPushAddress(destination);
TextSecureAddress address = getPushAddress(recipients.getPrimaryRecipient());
List<TextSecureAttachment> attachments = getAttachments(masterSecret, message); List<TextSecureAttachment> attachments = getAttachments(masterSecret, message);
String body = PartParser.getMessageText(message.getBody()); String body = PartParser.getMessageText(message.getBody());
TextSecureMessage mediaMessage = new TextSecureMessage(message.getSentTimestamp(), attachments, body); TextSecureMessage mediaMessage = new TextSecureMessage(message.getSentTimestamp(), attachments, body);
@ -129,7 +126,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
Log.w(TAG, e); Log.w(TAG, e);
if (isSmsFallbackSupported) fallbackOrAskApproval(masterSecret, message, destination); if (isSmsFallbackSupported) fallbackOrAskApproval(masterSecret, message, destination);
else database.markAsSentFailed(messageId); else database.markAsSentFailed(messageId);
} catch (IOException | RecipientFormattingException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
if (isSmsFallbackSupported) fallbackOrAskApproval(masterSecret, message, destination); if (isSmsFallbackSupported) fallbackOrAskApproval(masterSecret, message, destination);
else throw new RetryLaterException(e); else throw new RetryLaterException(e);
@ -140,25 +137,18 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
private void fallbackOrAskApproval(MasterSecret masterSecret, SendReq mediaMessage, String destination) private void fallbackOrAskApproval(MasterSecret masterSecret, SendReq mediaMessage, String destination)
throws SecureFallbackApprovalException, InsecureFallbackApprovalException throws SecureFallbackApprovalException, InsecureFallbackApprovalException
{ {
try { boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination, true);
Recipient recipient = RecipientFactory.getRecipientsFromString(context, destination, false).getPrimaryRecipient();
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination, true);
AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret);
if (!isSmsFallbackApprovalRequired) { if (!isSmsFallbackApprovalRequired) {
Log.w(TAG, "Falling back to MMS"); Log.w(TAG, "Falling back to MMS");
DatabaseFactory.getMmsDatabase(context).markAsForcedSms(mediaMessage.getDatabaseMessageId()); DatabaseFactory.getMmsDatabase(context).markAsForcedSms(mediaMessage.getDatabaseMessageId());
ApplicationContext.getInstance(context).getJobManager().add(new MmsSendJob(context, messageId)); ApplicationContext.getInstance(context).getJobManager().add(new MmsSendJob(context, messageId));
} else if (!axolotlStore.containsSession(recipient.getRecipientId(), TextSecureAddress.DEFAULT_DEVICE_ID)) { } else if (!SessionUtil.hasSession(context, masterSecret, destination)) {
Log.w(TAG, "Marking message as pending insecure SMS fallback"); Log.w(TAG, "Marking message as pending insecure SMS fallback");
throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS"); throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS");
} else { } else {
Log.w(TAG, "Marking message as pending secure SMS fallback"); Log.w(TAG, "Marking message as pending secure SMS fallback");
throw new SecureFallbackApprovalException("Pending user approval for fallback secure to SMS"); throw new SecureFallbackApprovalException("Pending user approval for fallback secure to SMS");
}
} catch (RecipientFormattingException rfe) {
Log.w(TAG, rfe);
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
} }
} }

View file

@ -9,13 +9,13 @@ import org.thoughtcrime.securesms.database.TextSecureDirectory;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
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;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream; import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream;
import org.whispersystems.textsecure.api.push.TextSecureAddress; import org.whispersystems.textsecure.api.push.TextSecureAddress;
@ -76,10 +76,10 @@ public abstract class PushSendJob extends SendJob {
} }
} }
protected TextSecureAddress getPushAddress(Recipient recipient) throws InvalidNumberException { protected TextSecureAddress getPushAddress(String number) throws InvalidNumberException {
String e164number = Util.canonicalizeNumber(context, recipient.getNumber()); String e164number = Util.canonicalizeNumber(context, number);
String relay = TextSecureDirectory.getInstance(context).getRelay(e164number); String relay = TextSecureDirectory.getInstance(context).getRelay(e164number);
return new TextSecureAddress(recipient.getRecipientId(), e164number, relay); return new TextSecureAddress(e164number, Optional.fromNullable(relay));
} }
protected boolean isSmsFallbackApprovalRequired(String destination, boolean media) { protected boolean isSmsFallbackApprovalRequired(String destination, boolean media) {

View file

@ -5,7 +5,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore; import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -15,12 +15,10 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; 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.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException;
import org.whispersystems.libaxolotl.state.AxolotlStore;
import org.whispersystems.textsecure.api.TextSecureMessageSender; import org.whispersystems.textsecure.api.TextSecureMessageSender;
import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
import org.whispersystems.textsecure.api.messages.TextSecureMessage; import org.whispersystems.textsecure.api.messages.TextSecureMessage;
@ -55,7 +53,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
} }
@Override @Override
public void onSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException, RecipientFormattingException { public void onSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId); SmsMessageRecord record = database.getMessage(masterSecret, messageId);
String destination = record.getIndividualRecipient().getNumber(); String destination = record.getIndividualRecipient().getNumber();
@ -111,7 +109,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
boolean isSmsFallbackSupported = isSmsFallbackSupported(context, destination, false); boolean isSmsFallbackSupported = isSmsFallbackSupported(context, destination, false);
try { try {
TextSecureAddress address = getPushAddress(message.getIndividualRecipient()); TextSecureAddress address = getPushAddress(message.getIndividualRecipient().getNumber());
TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret); TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
if (message.isEndSession()) { if (message.isEndSession()) {
@ -138,15 +136,14 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
private void fallbackOrAskApproval(MasterSecret masterSecret, SmsMessageRecord smsMessage, String destination) private void fallbackOrAskApproval(MasterSecret masterSecret, SmsMessageRecord smsMessage, String destination)
throws SecureFallbackApprovalException, InsecureFallbackApprovalException throws SecureFallbackApprovalException, InsecureFallbackApprovalException
{ {
Recipient recipient = smsMessage.getIndividualRecipient(); Recipient recipient = smsMessage.getIndividualRecipient();
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination, false); boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination, false);
AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret);
if (!isSmsFallbackApprovalRequired) { if (!isSmsFallbackApprovalRequired) {
Log.w(TAG, "Falling back to SMS"); Log.w(TAG, "Falling back to SMS");
DatabaseFactory.getSmsDatabase(context).markAsForcedSms(smsMessage.getId()); DatabaseFactory.getSmsDatabase(context).markAsForcedSms(smsMessage.getId());
ApplicationContext.getInstance(context).getJobManager().add(new SmsSendJob(context, messageId, destination)); ApplicationContext.getInstance(context).getJobManager().add(new SmsSendJob(context, messageId, destination));
} else if (!axolotlStore.containsSession(recipient.getRecipientId(), TextSecureAddress.DEFAULT_DEVICE_ID)) { } else if (!SessionUtil.hasSession(context, masterSecret, recipient)) {
Log.w(TAG, "Marking message as pending insecure fallback."); Log.w(TAG, "Marking message as pending insecure fallback.");
throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS"); throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS");
} else { } else {

View file

@ -83,7 +83,7 @@ public class SmsSentJob extends MasterSecretJob {
if (record != null && record.isEndSession()) { if (record != null && record.isEndSession()) {
Log.w(TAG, "Ending session..."); Log.w(TAG, "Ending session...");
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
sessionStore.deleteAllSessions(record.getIndividualRecipient().getRecipientId()); sessionStore.deleteAllSessions(record.getIndividualRecipient().getNumber());
SecurityEvent.broadcastSecurityUpdateEvent(context, record.getThreadId()); SecurityEvent.broadcastSecurityUpdateEvent(context, record.getThreadId());
} }

View file

@ -49,7 +49,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; 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.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -311,18 +310,10 @@ public class MessageNotifier {
reader = DatabaseFactory.getPushDatabase(context).readerFor(cursor); reader = DatabaseFactory.getPushDatabase(context).readerFor(cursor);
while ((envelope = reader.getNext()) != null) { while ((envelope = reader.getNext()) != null) {
Recipients recipients; Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
Recipient recipient = recipients.getPrimaryRecipient();
try { long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
} catch (RecipientFormattingException e) {
Log.w("MessageNotifier", e);
recipients = new Recipients(Recipient.getUnknownRecipient(context));
}
Recipient recipient = recipients.getPrimaryRecipient();
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null)); notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null));

View file

@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.api.TextSecureMessageSender; import org.whispersystems.textsecure.api.TextSecureMessageSender;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
public class SecurityEventListener implements TextSecureMessageSender.EventListener { public class SecurityEventListener implements TextSecureMessageSender.EventListener {
@ -19,8 +20,8 @@ public class SecurityEventListener implements TextSecureMessageSender.EventListe
} }
@Override @Override
public void onSecurityEvent(long recipientId) { public void onSecurityEvent(TextSecureAddress textSecureAddress) {
Recipients recipients = RecipientFactory.getRecipientsForIds(context, new long[]{recipientId}, false); Recipients recipients = RecipientFactory.getRecipientsFromString(context, textSecureAddress.getNumber(), false);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
SecurityEvent.broadcastSecurityUpdateEvent(context, threadId); SecurityEvent.broadcastSecurityUpdateEvent(context, threadId);

View file

@ -17,13 +17,13 @@
package org.thoughtcrime.securesms.recipients; package org.thoughtcrime.securesms.recipients;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase; import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -67,14 +67,8 @@ public class RecipientFactory {
return provider.getRecipient(context, recipientId, asynchronous); return provider.getRecipient(context, recipientId, asynchronous);
} }
public static Recipients getRecipientsFromString(Context context, String rawText, boolean asynchronous) public static Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
throws RecipientFormattingException List<Recipient> results = new LinkedList<>();
{
if (rawText == null) {
throw new RecipientFormattingException("Null recipient string specified");
}
List<Recipient> results = new LinkedList<Recipient>();
StringTokenizer tokenizer = new StringTokenizer(rawText, ","); StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
while (tokenizer.hasMoreTokens()) { while (tokenizer.hasMoreTokens()) {

View file

@ -234,29 +234,25 @@ public class RegistrationService extends Service {
throws IOException throws IOException
{ {
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number)); setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
try { Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient();
Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient(); IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this, masterSecret);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this, masterSecret); List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this, masterSecret);
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this, masterSecret); PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret); SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, masterSecret, identityKey);
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, masterSecret, identityKey); accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number)); setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID); String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
accountManager.setGcmId(Optional.of(gcmRegistrationId)); accountManager.setGcmId(Optional.of(gcmRegistrationId));
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId); TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
TextSecurePreferences.setWebsocketRegistered(this, true); TextSecurePreferences.setWebsocketRegistered(this, true);
DatabaseFactory.getIdentityDatabase(this).saveIdentity(masterSecret, self.getRecipientId(), identityKey.getPublicKey()); DatabaseFactory.getIdentityDatabase(this).saveIdentity(masterSecret, self.getRecipientId(), identityKey.getPublicKey());
DirectoryHelper.refreshDirectory(this, accountManager, number); DirectoryHelper.refreshDirectory(this, accountManager, number);
DirectoryRefreshListener.schedule(this); DirectoryRefreshListener.schedule(this);
} catch (RecipientFormattingException e) {
throw new IOException(e);
}
} }
private synchronized String waitForChallenge() throws AccountVerificationTimeoutException { private synchronized String waitForChallenge() throws AccountVerificationTimeoutException {

View file

@ -18,19 +18,15 @@
package org.thoughtcrime.securesms.util; package org.thoughtcrime.securesms.util;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.ImageView; import android.widget.ImageView;
import com.makeramen.RoundedDrawable; import com.makeramen.RoundedDrawable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient; 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 java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -59,28 +55,24 @@ public class BitmapWorkerRunnable implements Runnable {
@Override @Override
public void run() { public void run() {
try { final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient();
final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient(); final Bitmap contactPhoto = recipient.getContactPhoto();
final Bitmap contactPhoto = recipient.getContactPhoto(); if (defaultPhoto == contactPhoto) {
if (defaultPhoto == contactPhoto) { return;
return; }
} if (recipient.getContactPhoto() != null) {
if (recipient.getContactPhoto() != null) { final ImageView imageView = imageViewReference.get();
final ImageView imageView = imageViewReference.get(); final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView);
final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView);
if (bitmapWorkerTask.getTag().equals(number) && imageView != null) { if (bitmapWorkerTask.getTag().equals(number) && imageView != null) {
final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), recipient.getContactPhoto()); final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), recipient.getContactPhoto());
imageView.post(new Runnable() { imageView.post(new Runnable() {
@Override @Override
public void run() { public void run() {
imageView.setImageDrawable(drawable); imageView.setImageDrawable(drawable);
} }
}); });
}
} }
} catch (RecipientFormattingException rfe) {
Log.w(TAG, "Couldn't get recipient from string", rfe);
} }
} }