Remove the Canonical Address Database

This was a holdover from Signal's origins as a pure SMS app.
It causes problems, depends on undefined device specific behavior,
and should no longer be necessary now that we have all the
information we need to E164 all numbers.

// FREEBIE
master
Moxie Marlinspike 2017-07-26 09:59:15 -07:00
parent e452862813
commit 737810475e
113 changed files with 2029 additions and 2130 deletions

View File

@ -203,7 +203,7 @@ android {
}
defaultConfig {
versionCode 275
versionCode 276
versionName "4.8.1"
minSdkVersion 9

View File

@ -17,6 +17,7 @@ import android.widget.AdapterView;
import android.widget.ListView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -24,6 +25,8 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import java.util.List;
public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme();
@ -105,7 +108,7 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Recipients recipients = ((BlockedContactListItem)view).getRecipients();
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses());
startActivity(intent);
}
@ -124,8 +127,10 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override
public void bindView(View view, Context context, Cursor cursor) {
String recipientIds = cursor.getString(1);
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, true);
String addressesConcat = cursor.getString(1);
List<Address> addresses = Address.fromSerializedList(addressesConcat, " ");
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), true);
((BlockedContactListItem) view).set(recipients);
}

View File

@ -8,12 +8,11 @@ import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsAddressDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@ -28,12 +27,9 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.state.IdentityKeyStore;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import java.io.IOException;
@ -53,10 +49,8 @@ public class ConfirmIdentityDialog extends AlertDialog {
{
super(context);
try {
Recipient recipient = RecipientFactory.getRecipientForId(context, mismatch.getRecipientId(), false);
Recipient recipient = RecipientFactory.getRecipientFor(context, mismatch.getAddress(), false);
String name = recipient.toShortString();
String number = Util.canonicalizeNumber(context, recipient.getNumber());
String introduction = String.format(context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed), name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
@ -68,11 +62,8 @@ public class ConfirmIdentityDialog extends AlertDialog {
setTitle(name);
setMessage(spannableString);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, number));
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, recipient.getAddress()));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
} catch (InvalidNumberException e) {
throw new AssertionError(e);
}
}
@Override
@ -91,13 +82,13 @@ public class ConfirmIdentityDialog extends AlertDialog {
private final MasterSecret masterSecret;
private final MessageRecord messageRecord;
private final IdentityKeyMismatch mismatch;
private final String number;
private final Address address;
private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, String number) {
private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
this.masterSecret = masterSecret;
this.messageRecord = messageRecord;
this.mismatch = mismatch;
this.number = number;
this.address = address;
}
@Override
@ -107,7 +98,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(number, 1);
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
@ -151,16 +142,16 @@ public class ConfirmIdentityDialog extends AlertDialog {
if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(),
mismatch.getAddress(),
mismatch.getIdentityKey());
Recipients recipients = mmsAddressDatabase.getRecipientsForId(messageRecord.getId());
if (recipients.isGroupRecipient()) MessageSender.resendGroupMessage(getContext(), masterSecret, messageRecord, mismatch.getRecipientId());
if (recipients.isGroupRecipient()) MessageSender.resendGroupMessage(getContext(), masterSecret, messageRecord, mismatch.getAddress());
else MessageSender.resend(getContext(), masterSecret, messageRecord);
} else {
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(),
mismatch.getAddress(),
mismatch.getIdentityKey());
MessageSender.resend(getContext(), masterSecret, messageRecord);
@ -173,13 +164,13 @@ public class ConfirmIdentityDialog extends AlertDialog {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(),
mismatch.getAddress(),
mismatch.getIdentityKey());
boolean legacy = !messageRecord.isContentBundleKeyExchange();
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
messageRecord.getIndividualRecipient().getNumber(),
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
messageRecord.getRecipientDeviceId(), "",
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody().getBody()) : null,
@ -189,8 +180,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId(),
messageRecord.getIndividualRecipient().getNumber()));
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
} catch (IOException e) {
throw new AssertionError(e);
}

View File

@ -97,6 +97,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -164,7 +165,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -191,7 +192,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
{
private static final String TAG = ConversationActivity.class.getSimpleName();
public static final String RECIPIENTS_EXTRA = "recipients";
public static final String ADDRESSES_EXTRA = "addresses";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String IS_ARCHIVED_EXTRA = "is_archived";
public static final String TEXT_EXTRA = "draft_text";
@ -409,7 +410,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
addAttachmentContactInfo(data.getData());
break;
case GROUP_EDIT:
recipients = RecipientFactory.getRecipientsForIds(this, data.getLongArrayExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA), true);
recipients = RecipientFactory.getRecipientsFor(this, RecipientFactory.getRecipientFor(this, (Address)data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true), true);
recipients.addListener(this);
titleView.setTitle(recipients);
setBlockedUserState(recipients, isSecureText, isDefaultSms);
@ -421,7 +422,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
break;
case ADD_CONTACT:
recipients = RecipientFactory.getRecipientsForIds(ConversationActivity.this, recipients.getIds(), true);
recipients = RecipientFactory.getRecipientsFor(this, recipients.getAddresses(), true);
recipients.addListener(this);
fragment.reloadList();
break;
@ -679,7 +680,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.appendInvite(inviteText);
} else {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:" + recipients.getPrimaryRecipient().getNumber()));
intent.setData(Uri.parse("smsto:" + recipients.getPrimaryRecipient().getAddress().serialize()));
intent.putExtra("sms_body", inviteText);
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
startActivity(intent);
@ -725,7 +726,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleViewMedia() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
intent.putExtra(MediaOverviewActivity.ADDRESSES_EXTRA, recipients.getAddresses());
startActivity(intent);
}
@ -746,7 +747,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onClick(DialogInterface dialog, int which) {
Context self = ConversationActivity.this;
try {
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getNumber());
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString());
DatabaseFactory.getGroupDatabase(self).setActive(groupId, false);
GroupContext context = GroupContext.newBuilder()
@ -756,7 +757,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis(), 0);
MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null);
DatabaseFactory.getGroupDatabase(self).remove(groupId, TextSecurePreferences.getLocalNumber(self));
DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self)));
initializeEnabledCheck();
} catch (IOException e) {
Log.w(TAG, e);
@ -771,7 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleEditPushGroup() {
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress());
startActivityForResult(intent, GROUP_EDIT);
}
@ -813,7 +814,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (isSecureText) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, recipient.getNumber());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress());
startService(intent);
Intent activityIntent = new Intent(this, WebRtcCallActivity.class);
@ -822,7 +823,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
try {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.getNumber()));
Uri.parse("tel:" + recipient.getAddress().serialize()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);
@ -838,9 +839,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleAddToContacts() {
if (recipients.getPrimaryRecipient().getAddress().isGroup()) return;
try {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getNumber());
if (recipients.getPrimaryRecipient().getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipients.getPrimaryRecipient().getAddress().toEmailString());
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipients.getPrimaryRecipient().getAddress().toPhoneString());
}
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
startActivityForResult(intent, ADD_CONTACT);
} catch (ActivityNotFoundException e) {
@ -1103,12 +1110,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (recipients.isGroupRecipient()) {
recipients = DatabaseFactory.getGroupDatabase(ConversationActivity.this)
.getGroupMembers(GroupUtil.getDecodedId(recipients.getPrimaryRecipient().getNumber()), false);
.getGroupMembers(GroupUtil.getDecodedId(recipients.getPrimaryRecipient().getAddress().toGroupString()), false);
}
for (long recipientId : recipients.getIds()) {
Log.w(TAG, "Loading identity for: " + recipientId);
identityRecordList.add(identityDatabase.getIdentity(recipientId));
for (Address recipientAddress : recipients.getAddresses()) {
Log.w(TAG, "Loading identity for: " + recipientAddress);
identityRecordList.add(identityDatabase.getIdentity(recipientAddress));
}
String message = null;
@ -1206,7 +1213,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onClick(View v) {
Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(RecipientPreferenceActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA,
isSecureText && !isSelfConversation());
@ -1253,7 +1260,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void initializeResources() {
if (recipients != null) recipients.removeListener(this);
recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getLongArrayExtra(RECIPIENTS_EXTRA), true);
recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
@ -1295,7 +1302,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRecipientPreferenceUpdate(final RecipientPreferenceEvent event) {
if (event.getRecipients().getSortedIdsString().equals(this.recipients.getSortedIdsString())) {
if (Arrays.equals(event.getRecipients().getAddresses(), this.recipients.getAddresses())) {
new RecipientPreferencesTask().execute(this.recipients);
}
}
@ -1319,9 +1326,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onReceive(Context context, Intent intent) {
Log.w(TAG, "Group update received...");
if (recipients != null) {
long[] ids = recipients.getIds();
Log.w(TAG, "Looking up new recipients...");
recipients = RecipientFactory.getRecipientsForIds(context, ids, true);
recipients = RecipientFactory.getRecipientsFor(context, recipients.getAddresses(), true);
recipients.addListener(ConversationActivity.this);
onModified(recipients);
fragment.reloadList();
@ -1501,7 +1507,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (!isGroupConversation()) return false;
try {
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getNumber());
byte[] groupId = GroupUtil.getDecodedId(getRecipients().getPrimaryRecipient().getAddress().toGroupString());
GroupRecord record = DatabaseFactory.getGroupDatabase(this).getGroup(groupId);
return record != null && record.isActive();
@ -1516,7 +1522,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (!recipients.isSingleRecipient()) return false;
if (recipients.getPrimaryRecipient().isGroupRecipient()) return false;
return Util.isOwnNumber(this, recipients.getPrimaryRecipient().getNumber());
return Util.isOwnNumber(this, recipients.getPrimaryRecipient().getAddress());
}
private boolean isGroupConversation() {
@ -2000,7 +2006,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
Optional<RecipientsPreferences> prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
.getRecipientsPreferences(recipients[0].getIds());
.getRecipientsPreferences(recipients[0].getAddresses());
return new Pair<>(recipients[0], prefs.orNull());
}
@ -2023,7 +2029,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : unverifiedIdentities) {
identityDatabase.setVerified(identityRecord.getRecipientId(),
identityDatabase.setVerified(identityRecord.getAddress(),
identityRecord.getIdentityKey(),
VerifiedStatus.DEFAULT);
}
@ -2046,7 +2052,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, "onClicked: " + unverifiedIdentities.size());
if (unverifiedIdentities.size() == 1) {
Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, unverifiedIdentities.get(0).getRecipientId());
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(0).getAddress());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(0).getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false);
@ -2055,7 +2061,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String[] unverifiedNames = new String[unverifiedIdentities.size()];
for (int i=0;i<unverifiedIdentities.size();i++) {
unverifiedNames[i] = RecipientFactory.getRecipientForId(ConversationActivity.this, unverifiedIdentities.get(i).getRecipientId(), false).toShortString();
unverifiedNames[i] = RecipientFactory.getRecipientFor(ConversationActivity.this, unverifiedIdentities.get(i).getAddress(), false).toShortString();
}
AlertDialog.Builder builder = new AlertDialog.Builder(ConversationActivity.this);
@ -2065,7 +2071,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, unverifiedIdentities.get(which).getRecipientId());
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(which).getAddress());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(which).getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false);

View File

@ -53,19 +53,14 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender;
@ -76,8 +71,6 @@ import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
@ -191,8 +184,8 @@ public class ConversationFragment extends Fragment
}
private void initializeResources() {
this.recipients = RecipientFactory.getRecipientsForIds(getActivity(), getActivity().getIntent().getLongArrayExtra("recipients"), true);
this.threadId = this.getActivity().getIntent().getLongExtra("thread_id", -1);
this.recipients = RecipientFactory.getRecipientsFor(getActivity(), Address.fromParcelable(getActivity().getIntent().getParcelableArrayExtra(ConversationActivity.ADDRESSES_EXTRA)), true);
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
this.firstLoad = true;
@ -366,7 +359,7 @@ public class ConversationFragment extends Fragment
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.RECIPIENTS_IDS_EXTRA, recipients.getIds());
intent.putExtra(MessageDetailsActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) && message.isPush());
startActivity(intent);
}

View File

@ -578,7 +578,7 @@ public class ConversationItem extends LinearLayout
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(slide.getUri(), slide.getContentType());
if (!messageRecord.isOutgoing()) intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipient.getRecipientId());
if (!messageRecord.isOutgoing()) intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, recipient.getAddress());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
@ -635,7 +635,7 @@ public class ConversationItem extends LinearLayout
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush());
intent.putExtra(MessageDetailsActivity.RECIPIENTS_IDS_EXTRA, conversationRecipients.getIds());
intent.putExtra(MessageDetailsActivity.ADDRESSES_EXTRA, conversationRecipients.getAddresses());
context.startActivity(intent);
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
handleApproveIdentity();
@ -680,7 +680,7 @@ public class ConversationItem extends LinearLayout
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SmsSendJob(context, messageRecord.getId(),
messageRecord.getIndividualRecipient().getNumber()));
messageRecord.getIndividualRecipient().getAddress().serialize()));
}
}
});

View File

@ -24,10 +24,10 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.util.Log;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -163,7 +162,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeen) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());

View File

@ -29,6 +29,7 @@ import android.view.ViewGroup;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
@ -79,8 +80,8 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
ThreadRecord record = getThreadRecord(cursor);
StringBuilder builder = new StringBuilder("" + record.getThreadId());
for (long recipientId : record.getRecipients().getIds()) {
builder.append("::").append(recipientId);
for (Address address : record.getRecipients().getAddresses()) {
builder.append("::").append(address.serialize());
}
return Conversions.byteArrayToLong(digest.digest(builder.toString().getBytes()));

View File

@ -56,7 +56,7 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
@Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType, long lastSeenTime) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);

View File

@ -84,7 +84,7 @@ public class ConversationPopupActivity extends ConversationActivity {
public void onSuccess(Long result) {
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, getRecipients().getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, getRecipients().getAddresses());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {

View File

@ -69,14 +69,14 @@ public class ConversationTitleView extends LinearLayout {
private void setRecipientTitle(Recipient recipient) {
if (!recipient.isGroupRecipient()) {
if (TextUtils.isEmpty(recipient.getName())) {
this.title.setText(recipient.getNumber());
this.title.setText(recipient.getAddress().serialize());
this.subtitle.setText(null);
this.subtitle.setVisibility(View.GONE);
} else {
this.title.setText(recipient.getName());
if (recipient.getCustomLabel() != null) this.subtitle.setText(recipient.getCustomLabel());
else this.subtitle.setText(recipient.getNumber());
else this.subtitle.setText(recipient.getAddress().serialize());
this.subtitle.setVisibility(View.VISIBLE);
}

View File

@ -221,7 +221,7 @@ public class ConversationUpdateItem extends LinearLayout
public void onSuccess(Optional<IdentityRecord> result) {
if (result.isPresent()) {
Intent intent = new Intent(getContext(), VerifyIdentityActivity.class);
intent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, sender.getRecipientId());
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress());
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey()));
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);

View File

@ -68,7 +68,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
public static final int CONTACTS_ACCOUNT_VERSION = 136;
public static final int MEDIA_DOWNLOAD_CONTROLS_VERSION = 151;
public static final int REDPHONE_SUPPORT_VERSION = 157;
// public static final int FINGERPRINTS_NON_BLOCKING_VESRION = 212;
public static final int NO_MORE_CANONICAL_DB_VERSION = 276;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -82,7 +82,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(MIGRATE_SESSION_PLAINTEXT);
add(MEDIA_DOWNLOAD_CONTROLS_VERSION);
add(REDPHONE_SUPPORT_VERSION);
// add(FINGERPRINTS_NON_BLOCKING_VESRION);
add(NO_MORE_CANONICAL_DB_VERSION);
}};
private MasterSecret masterSecret;
@ -233,10 +233,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
.add(new DirectoryRefreshJob(getApplicationContext()));
}
// if (params[0] < FINGERPRINTS_NON_BLOCKING_VESRION) {
// TextSecurePreferences.setBlockingIdentityUpdates(getApplicationContext(), true);
// }
return null;
}
@ -274,8 +270,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
ApplicationContext.getInstance(getApplicationContext())
.getJobManager()
.add(new PushDecryptJob(getApplicationContext(),
pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)),
pushReader.getString(pushReader.getColumnIndexOrThrow(PushDatabase.SOURCE))));
pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID))));
}
} finally {
if (pushReader != null)

View File

@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@ -66,12 +67,11 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
@ -95,8 +95,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private final static String TAG = GroupCreateActivity.class.getSimpleName();
public static final String GROUP_RECIPIENT_EXTRA = "group_recipient";
public static final String GROUP_THREAD_EXTRA = "group_thread";
public static final String GROUP_ADDRESS_EXTRA = "group_recipient";
public static final String GROUP_THREAD_EXTRA = "group_thread";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -174,9 +174,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static boolean isActiveInDirectory(Context context, Recipient recipient) {
try {
return TextSecureDirectory.getInstance(context)
.isSecureTextSupported(Util.canonicalizeNumber(context, recipient.getNumber()));
} catch (NotInDirectoryException | InvalidNumberException e) {
return TextSecureDirectory.getInstance(context).isSecureTextSupported(recipient.getAddress());
} catch (NotInDirectoryException e) {
return false;
}
}
@ -213,11 +212,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void initializeExistingGroup() {
final String encodedGroupId = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(GROUP_RECIPIENT_EXTRA, -1), true)
.getNumber();
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
byte[] groupId;
try {
groupId = GroupUtil.getDecodedId(encodedGroupId);
if (groupAddress != null) groupId = GroupUtil.getDecodedId(groupAddress.toGroupString());
else groupId = null;
} catch (IOException ioe) {
Log.w(TAG, "Couldn't decode the encoded groupId passed in via intent", ioe);
groupId = null;
@ -286,7 +287,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
startActivity(intent);
finish();
}
@ -310,9 +311,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
switch (reqCode) {
case PICK_CONTACT:
List<String> selected = data.getStringArrayListExtra("contacts");
for (String contact : selected) {
final Recipient recipient = RecipientFactory.getRecipientsFromString(this, contact, false).getPrimaryRecipient();
if (recipient != null) addSelectedContacts(recipient);
Address address = Address.fromExternal(this, contact);
Recipient recipient = RecipientFactory.getRecipientFor(this, address, false);
addSelectedContacts(recipient);
}
break;
@ -470,7 +474,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_RECIPIENT_EXTRA, result.get().getGroupRecipient().getIds());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getPrimaryRecipient().getAddress());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
@ -508,16 +512,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
final List<Result> results = new LinkedList<>();
for (Recipient recipient : recipients) {
boolean isPush = isActiveInDirectory(activity, recipient);
String recipientE164 = null;
try {
recipientE164 = Util.canonicalizeNumber(activity, recipient.getNumber());
} catch (InvalidNumberException ine) { /* do nothing */ }
boolean isPush = isActiveInDirectory(activity, recipient);
if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
recipient.getNumber())));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipientE164)) {
recipient.toShortString())));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
} else {
results.add(new Result(recipient, isPush, null));

View File

@ -14,9 +14,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.util.LinkedList;
@ -40,7 +38,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
@Override
protected Recipients doInBackground(Void... params) {
try {
String groupId = recipients.getPrimaryRecipient().getNumber();
String groupId = recipients.getPrimaryRecipient().getAddress().toGroupString();
return DatabaseFactory.getGroupDatabase(context)
.getGroupMembers(GroupUtil.getDecodedId(groupId), true);
} catch (IOException e) {
@ -85,7 +83,11 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
ContactsContract.QuickContact.MODE_LARGE, null);
} else {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getNumber());
if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
}
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
context.startActivity(intent);
}
@ -134,15 +136,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
}
private boolean isLocalNumber(Recipient recipient) {
try {
String localNumber = TextSecurePreferences.getLocalNumber(context);
String e164Number = Util.canonicalizeNumber(context, recipient.getNumber());
return e164Number != null && e164Number.equals(localNumber);
} catch (InvalidNumberException e) {
Log.w(TAG, e);
return false;
}
return Util.isOwnNumber(context, recipient.getAddress());
}
}
}

View File

@ -27,6 +27,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -230,10 +231,10 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (context == null) return null;
for (String number : numbers) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, number)}, false);
if (recipients.getPrimaryRecipient() != null) {
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds());
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getAddresses());
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message, subscriptionId), -1L, true, null);

View File

@ -21,7 +21,6 @@ import android.content.Intent;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@ -33,8 +32,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.MediaUtil;
public class MediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
@ -93,14 +90,10 @@ public class MediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, threadId);
if (!TextUtils.isEmpty(mediaRecord.getAddress())) {
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(),
mediaRecord.getAddress(),
true);
if (recipients != null && recipients.getPrimaryRecipient() != null) {
intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
}
if (mediaRecord.getAddress() != null) {
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, mediaRecord.getAddress());
}
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
getContext().startActivity(intent);
}

View File

@ -38,12 +38,12 @@ import android.view.WindowManager;
import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
@ -58,7 +58,7 @@ import java.util.List;
public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private final static String TAG = MediaOverviewActivity.class.getSimpleName();
public static final String RECIPIENT_EXTRA = "recipient";
public static final String ADDRESSES_EXTRA = "addresses";
public static final String THREAD_ID_EXTRA = "thread_id";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -68,7 +68,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
private RecyclerView gridView;
private GridLayoutManager gridManager;
private TextView noImages;
private Recipient recipient;
private Recipients recipients;
private long threadId;
@Override
@ -113,9 +113,9 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
}
private void initializeActionBar() {
getSupportActionBar().setTitle(recipient == null
getSupportActionBar().setTitle(recipients == null
? getString(R.string.AndroidManifest__all_media)
: getString(R.string.AndroidManifest__all_media_named, recipient.toShortString()));
: getString(R.string.AndroidManifest__all_media_named, recipients.toShortString()));
}
@Override
@ -132,19 +132,20 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity i
gridView.setLayoutManager(gridManager);
gridView.setHasFixedSize(true);
final long recipientId = getIntent().getLongExtra(RECIPIENT_EXTRA, -1);
if (recipientId > -1) {
recipient = RecipientFactory.getRecipientForId(this, recipientId, true);
} else if (threadId > -1){
recipient = DatabaseFactory.getThreadDatabase(this).getRecipientsForThreadId(threadId).getPrimaryRecipient();
Address[] addresses = Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA));
if (addresses != null) {
recipients = RecipientFactory.getRecipientsFor(this, addresses, true);
} else if (threadId > -1) {
recipients = DatabaseFactory.getThreadDatabase(this).getRecipientsForThreadId(threadId);
} else {
recipient = null;
recipients = null;
}
if (recipient != null) {
recipient.addListener(new RecipientModifiedListener() {
if (recipients != null) {
recipients.addListener(new Recipients.RecipientsModifiedListener() {
@Override
public void onModified(Recipient recipient) {
public void onModified(Recipients recipients) {
initializeActionBar();
}
});

View File

@ -34,6 +34,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ZoomingImageView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
@ -52,7 +53,7 @@ import java.io.IOException;
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener {
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
public static final String RECIPIENT_EXTRA = "recipient";
public static final String ADDRESS_EXTRA = "address";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String DATE_EXTRA = "date";
public static final String SIZE_EXTRA = "size";
@ -143,7 +144,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
private void initializeResources() {
final long recipientId = getIntent().getLongExtra(RECIPIENT_EXTRA, -1);
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
mediaUri = getIntent().getData();
mediaType = getIntent().getType();
@ -151,8 +152,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
size = getIntent().getLongExtra(SIZE_EXTRA, 0);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
if (recipientId > -1) {
recipient = RecipientFactory.getRecipientForId(this, recipientId, true);
if (address != null) {
recipient = RecipientFactory.getRecipientFor(this, address, true);
recipient.addListener(this);
} else {
recipient = null;

View File

@ -36,6 +36,7 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@ -73,7 +74,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
public final static String THREAD_ID_EXTRA = "thread_id";
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
public final static String TYPE_EXTRA = "type";
public final static String RECIPIENTS_IDS_EXTRA = "recipients_ids";
public final static String ADDRESSES_EXTRA = "addresses";
private MasterSecret masterSecret;
private long threadId;
@ -138,7 +139,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private void initializeActionBar() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Recipients recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getLongArrayExtra(RECIPIENTS_IDS_EXTRA), true);
Recipients recipients = RecipientFactory.getRecipientsFor(this, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true);
recipients.addListener(this);
setActionBarColor(recipients.getColor());
@ -355,9 +356,9 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
recipients = intermediaryRecipients;
} else {
try {
String groupId = intermediaryRecipients.getPrimaryRecipient().getNumber();
Address groupId = intermediaryRecipients.getPrimaryRecipient().getAddress();
recipients = DatabaseFactory.getGroupDatabase(context)
.getGroupMembers(GroupUtil.getDecodedId(groupId), false);
.getGroupMembers(GroupUtil.getDecodedId(groupId.toGroupString()), false);
} catch (IOException e) {
Log.w(TAG, e);
recipients = RecipientFactory.getRecipientsFor(MessageDetailsActivity.this, new LinkedList<Recipient>(), false);

View File

@ -11,6 +11,10 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Conversions;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
@ -43,7 +47,11 @@ public class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsLi
@Override
public long getItemId(int position) {
return recipients.getRecipientsList().get(position).getRecipientId();
try {
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(recipients.getRecipientsList().get(position).getAddress().serialize().getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
@Override

View File

@ -137,7 +137,7 @@ public class MessageRecipientListItem extends RelativeLayout
private NetworkFailure getNetworkFailure(final MessageRecord record) {
if (record.hasNetworkFailures()) {
for (final NetworkFailure failure : record.getNetworkFailures()) {
if (failure.getRecipientId() == recipient.getRecipientId()) {
if (failure.getAddress().equals(recipient.getAddress())) {
return failure;
}
}
@ -148,7 +148,7 @@ public class MessageRecipientListItem extends RelativeLayout
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
if (record.isIdentityMismatchFailure()) {
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
if (mismatch.getRecipientId() == recipient.getRecipientId()) {
if (mismatch.getAddress().equals(recipient.getAddress())) {
return mismatch;
}
}
@ -188,7 +188,7 @@ public class MessageRecipientListItem extends RelativeLayout
mmsDatabase.removeFailure(record.getId(), failure);
if (record.getRecipients().isGroupRecipient()) {
MessageSender.resendGroupMessage(getContext(), masterSecret, record, failure.getRecipientId());
MessageSender.resendGroupMessage(getContext(), masterSecret, record, failure.getAddress());
} else {
MessageSender.resend(getContext(), masterSecret, record);
}

View File

@ -25,6 +25,7 @@ import android.view.MenuItem;
import android.view.View;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -49,10 +50,10 @@ public class NewConversationActivity extends ContactSelectionActivity {
@Override
public void onContactSelected(String number) {
Recipients recipients = RecipientFactory.getRecipientsFromString(this, number, true);
Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, number)}, true);
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
intent.setDataAndType(getIntent().getData(), getIntent().getType());

View File

@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.color.MaterialColors;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase;
@ -48,7 +49,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.ExecutionException;
@ -57,7 +57,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
{
private static final String TAG = RecipientPreferenceActivity.class.getSimpleName();
public static final String RECIPIENTS_EXTRA = "recipient_ids";
public static final String ADDRESSES_EXTRA = "recipient_addresses";
public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number";
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
@ -86,8 +86,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public void onCreate(Bundle instanceState, @NonNull MasterSecret masterSecret) {
setContentView(R.layout.recipient_preference_activity);
long[] recipientIds = getIntent().getLongArrayExtra(RECIPIENTS_EXTRA);
Recipients recipients = RecipientFactory.getRecipientsForIds(this, recipientIds, true);
Address[] addresses = Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA));
Recipients recipients = RecipientFactory.getRecipientsFor(this, addresses, true);
initializeToolbar();
initializeReceivers();
@ -95,7 +95,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
recipients.addListener(this);
Bundle bundle = new Bundle();
bundle.putLongArray(RECIPIENTS_EXTRA, recipientIds);
bundle.putParcelableArray(ADDRESSES_EXTRA, addresses);
initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), masterSecret, null, bundle);
}
@ -149,7 +149,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.staleReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Recipients recipients = RecipientFactory.getRecipientsForIds(context, getIntent().getLongArrayExtra(RECIPIENTS_EXTRA), true);
Recipients recipients = RecipientFactory.getRecipientsFor(context, Address.fromParcelable(getIntent().getParcelableArrayExtra(ADDRESSES_EXTRA)), true);
recipients.addListener(RecipientPreferenceActivity.this);
onModified(recipients);
}
@ -234,9 +234,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
private void initializeRecipients() {
this.recipients = RecipientFactory.getRecipientsForIds(getActivity(),
getArguments().getLongArray(RECIPIENTS_EXTRA),
true);
this.recipients = RecipientFactory.getRecipientsFor(getActivity(),
Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)),
true);
this.recipients.addListener(this);
@ -244,7 +244,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public void onReceive(Context context, Intent intent) {
recipients.removeListener(RecipientPreferenceFragment.this);
recipients = RecipientFactory.getRecipientsForIds(getActivity(), getArguments().getLongArray(RECIPIENTS_EXTRA), true);
recipients = RecipientFactory.getRecipientsFor(getActivity(), Address.fromParcelable(getArguments().getParcelableArray(ADDRESSES_EXTRA)), true);
onModified(recipients);
}
};
@ -411,7 +411,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(context, recipients.getPrimaryRecipient().getRecipientId()));
.add(new MultiDeviceContactUpdateJob(context, recipients.getPrimaryRecipient().getAddress()));
}
return null;
}
@ -471,7 +471,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
@Override
public boolean onPreferenceClick(Preference preference) {
Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class);
verifyIdentityIntent.putExtra(VerifyIdentityActivity.RECIPIENT_ID_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipients.getPrimaryRecipient().getAddress());
verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
startActivity(verifyIdentityIntent);

View File

@ -23,6 +23,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Process;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
@ -35,9 +36,9 @@ import android.view.View;
import android.view.ViewGroup;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -59,9 +60,9 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
{
private static final String TAG = ShareActivity.class.getSimpleName();
public static final String EXTRA_THREAD_ID = "thread_id";
public static final String EXTRA_RECIPIENT_IDS = "recipient_ids";
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
public static final String EXTRA_THREAD_ID = "thread_id";
public static final String EXTRA_ADDRESSES_MARSHALLED = "addresses";
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
private final DynamicTheme dynamicTheme = new DynamicTheme ();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -93,6 +94,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onNewIntent(Intent intent) {
Log.w(TAG, "onNewIntent()");
super.onNewIntent(intent);
setIntent(intent);
initializeMedia();
@ -100,6 +102,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
public void onResume() {
Log.w(TAG, "onResume()");
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
@ -163,15 +166,24 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType) {
createConversation(threadId, recipients, distributionType);
createConversation(threadId, recipients.getAddresses(), distributionType);
}
private void handleResolvedMedia(Intent intent, boolean animate) {
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
long[] recipientIds = intent.getLongArrayExtra(EXTRA_RECIPIENT_IDS);
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
Address[] addresses = null;
boolean hasResolvedDestination = threadId != -1 && recipientIds != null && distributionType != -1;
if (intent.hasExtra(EXTRA_ADDRESSES_MARSHALLED)) {
Parcel parcel = Parcel.obtain();
byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESSES_MARSHALLED);
parcel.unmarshall(marshalled, 0, marshalled.length);
parcel.setDataPosition(0);
addresses = parcel.createTypedArray(Address.CREATOR);
parcel.recycle();
}
boolean hasResolvedDestination = threadId != -1 && addresses != null && distributionType != -1;
if (!hasResolvedDestination && animate) {
ViewUtil.fadeIn(fragmentContainer, 300);
@ -180,13 +192,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
fragmentContainer.setVisibility(View.VISIBLE);
progressWheel.setVisibility(View.GONE);
} else {
createConversation(threadId, RecipientFactory.getRecipientsForIds(this, recipientIds, true), distributionType);
createConversation(threadId, addresses, distributionType);
}
}
private void createConversation(long threadId, Recipients recipients, int distributionType) {
private void createConversation(long threadId, Address[] addresses, int distributionType) {
final Intent intent = getBaseShareIntent(ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, addresses);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);

View File

@ -6,9 +6,11 @@ import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
@ -38,19 +40,20 @@ public class SmsSendtoActivity extends Activity {
destination = getDestinationForView(original);
}
Recipients recipients = RecipientFactory.getRecipientsFromString(this, destination.getDestination(), true);
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
final Intent nextIntent;
if (recipients.isEmpty()) {
if (TextUtils.isEmpty(destination.destination)) {
nextIntent = new Intent(this, NewConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
} else {
Recipients recipients = RecipientFactory.getRecipientsFor(this, new Address[] {Address.fromExternal(this, destination.getDestination())}, true);
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
nextIntent = new Intent(this, ConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
nextIntent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
nextIntent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
}
return nextIntent;
}

View File

@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
@ -84,7 +85,6 @@ import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
@ -100,9 +100,9 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
private static final String TAG = VerifyIdentityActivity.class.getSimpleName();
public static final String RECIPIENT_ID_EXTRA = "recipient_id";
public static final String IDENTITY_EXTRA = "recipient_identity";
public static final String VERIFIED_EXTRA = "verified_state";
public static final String ADDRESS_EXTRA = "address";
public static final String IDENTITY_EXTRA = "recipient_identity";
public static final String VERIFIED_EXTRA = "verified_state";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -118,31 +118,26 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number);
Recipient recipient = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(RECIPIENT_ID_EXTRA, -1), true);
recipient.addListener(this);
Recipient recipient = RecipientFactory.getRecipientFor(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
recipient.addListener(this);
setActionBarNotificationBarColor(recipient.getColor());
setActionBarNotificationBarColor(recipient.getColor());
Bundle extras = new Bundle();
extras.putLong(VerifyDisplayFragment.REMOTE_RECIPIENT_ID, getIntent().getLongExtra(RECIPIENT_ID_EXTRA, -1));
extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA));
extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, Util.canonicalizeNumber(this, recipient.getNumber()));
extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this));
extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false));
Bundle extras = new Bundle();
extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA));
extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA));
extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString());
extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this));
extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false));
scanFragment.setScanListener(this);
displayFragment.setClickListener(this);
scanFragment.setScanListener(this);
displayFragment.setClickListener(this);
initFragment(android.R.id.content, displayFragment, masterSecret, dynamicLanguage.getCurrentLocale(), extras);
} catch (InvalidNumberException e) {
Log.w(TAG, e);
finish();
}
initFragment(android.R.id.content, displayFragment, masterSecret, dynamicLanguage.getCurrentLocale(), extras);
}
@Override
@ -198,12 +193,12 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
public static class VerifyDisplayFragment extends Fragment implements Recipient.RecipientModifiedListener, CompoundButton.OnCheckedChangeListener {
public static final String REMOTE_RECIPIENT_ID = "remote_recipient_id";
public static final String REMOTE_NUMBER = "remote_number";
public static final String REMOTE_IDENTITY = "remote_identity";
public static final String LOCAL_IDENTITY = "local_identity";
public static final String LOCAL_NUMBER = "local_number";
public static final String VERIFIED_STATE = "verified_state";
public static final String REMOTE_ADDRESS = "remote_address";
public static final String REMOTE_NUMBER = "remote_number";
public static final String REMOTE_IDENTITY = "remote_identity";
public static final String LOCAL_IDENTITY = "local_identity";
public static final String LOCAL_NUMBER = "local_number";
public static final String VERIFIED_STATE = "verified_state";
private MasterSecret masterSecret;
private Recipient recipient;
@ -263,12 +258,20 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Address address = getArguments().getParcelable(REMOTE_ADDRESS);
IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY);
IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY);
if (address == null) throw new AssertionError("Address required");
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
this.masterSecret = getArguments().getParcelable("master_secret");
this.localNumber = getArguments().getString(LOCAL_NUMBER);
this.localIdentity = ((IdentityKeyParcelable)getArguments().getParcelable(LOCAL_IDENTITY)).get();
this.localIdentity = localIdentityParcelable.get();
this.remoteNumber = getArguments().getString(REMOTE_NUMBER);
this.recipient = RecipientFactory.getRecipientForId(getActivity(), getArguments().getLong(REMOTE_RECIPIENT_ID), true);
this.remoteIdentity = ((IdentityKeyParcelable)getArguments().getParcelable(REMOTE_IDENTITY)).get();
this.recipient = RecipientFactory.getRecipientFor(getActivity(), address, true);
this.remoteIdentity = remoteIdentityParcelable.get();
this.recipient.addListener(this);
@ -589,15 +592,15 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Recipient... params) {
synchronized (SESSION_LOCK) {
if (isChecked) {
Log.w(TAG, "Saving identity: " + params[0].getRecipientId());
Log.w(TAG, "Saving identity: " + params[0].getAddress());
DatabaseFactory.getIdentityDatabase(getActivity())
.saveIdentity(params[0].getRecipientId(),
.saveIdentity(params[0].getAddress(),
remoteIdentity,
VerifiedStatus.VERIFIED, false,
System.currentTimeMillis(), true);
} else {
DatabaseFactory.getIdentityDatabase(getActivity())
.setVerified(params[0].getRecipientId(),
.setVerified(params[0].getAddress(),
remoteIdentity,
VerifiedStatus.DEFAULT);
}
@ -605,7 +608,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
ApplicationContext.getInstance(getActivity())
.getJobManager()
.add(new MultiDeviceVerifiedUpdateJob(getActivity(),
recipient.getNumber(),
recipient.getAddress(),
remoteIdentity,
isChecked ? VerifiedStatus.VERIFIED :
VerifiedStatus.DEFAULT));

View File

@ -258,11 +258,11 @@ public class WebRtcCallActivity extends Activity {
public void onClick(View v) {
synchronized (SESSION_LOCK) {
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getNumber(), 1), theirIdentity, true);
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true);
}
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, recipient.getNumber());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress());
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
startService(intent);
}

View File

@ -63,7 +63,11 @@ public class AvatarImageView extends ImageView {
ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else if (recipient != null) {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getNumber());
if (recipient.getAddress().isEmail()) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
}
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
getContext().startActivity(intent);
}

View File

@ -18,31 +18,28 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
/**
* Panel component combining both an editable field with a button for
@ -74,26 +71,11 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
initialize();
}
public void addRecipient(String name, String number) {
if (name != null) recipientsText.append(name + "< " + number + ">, ");
else recipientsText.append(number + ", ");
}
public void addRecipients(Recipients recipients) {
List<Recipient> recipientList = recipients.getRecipientsList();
Iterator<Recipient> iterator = recipientList.iterator();
while (iterator.hasNext()) {
Recipient recipient = iterator.next();
addRecipient(recipient.getName(), recipient.getNumber());
}
}
public Recipients getRecipients() throws RecipientFormattingException {
String rawText = recipientsText.getText().toString();
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, true);
String rawText = recipientsText.getText().toString();
Recipients recipients = getRecipientsFromString(getContext(), rawText, true);
if (recipients.isEmpty())
if (recipients == null || recipients.isEmpty())
throw new RecipientFormattingException("Recipient List Is Empty!");
return recipients;
@ -151,7 +133,40 @@ public class PushRecipientsPanel extends RelativeLayout implements RecipientsMod
});
}
@Override public void onModified(Recipients recipients) {
private @Nullable Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
List<Address> addresses = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (!TextUtils.isEmpty(token)) {
if (hasBracketedNumber(token)) addresses.add(Address.fromExternal(context, parseBracketedNumber(token)));
else addresses.add(Address.fromExternal(context, token));
}
}
if (addresses.size() == 0) return null;
else return RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), asynchronous);
}
private boolean hasBracketedNumber(String recipient) {
int openBracketIndex = recipient.indexOf('<');
return (openBracketIndex != -1) &&
(recipient.indexOf('>', openBracketIndex) != -1);
}
private String parseBracketedNumber(String recipient) {
int begin = recipient.indexOf('<');
int end = recipient.indexOf('>', begin);
String value = recipient.substring(begin + 1, end);
return value;
}
@Override
public void onModified(Recipients recipients) {
recipientsText.populate(recipients);
}

View File

@ -1,176 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.RelativeLayout;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Panel component combining both an editable field with a button for
* a list-based contact selector.
*
* @author Moxie Marlinspike
*/
public class SingleRecipientPanel extends RelativeLayout implements RecipientsModifiedListener {
private final String TAG = SingleRecipientPanel.class.getSimpleName();
private RecipientsPanelChangedListener panelChangeListener;
private RecipientsEditor recipientsText;
private View panel;
private static final int RECIPIENTS_MAX_LENGTH = 312;
public SingleRecipientPanel(Context context) {
super(context);
initialize();
}
public SingleRecipientPanel(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public SingleRecipientPanel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
public void addRecipient(String name, String number) {
Log.i(TAG, "addRecipient for " + name + "/" + number);
if (name != null) recipientsText.append(name + "< " + number + ">, ");
else recipientsText.append(number + ", ");
}
public void addRecipients(Recipients recipients) {
List<Recipient> recipientList = recipients.getRecipientsList();
Iterator<Recipient> iterator = recipientList.iterator();
while (iterator.hasNext()) {
Recipient recipient = iterator.next();
addRecipient(recipient.getName(), recipient.getNumber());
}
}
public void addContacts(List<ContactAccessor.ContactData> contacts) {
for (ContactAccessor.ContactData contact : contacts) {
for (ContactAccessor.NumberData number : contact.numbers) {
addRecipient(contact.name, number.number);
}
}
}
public Recipients getRecipients() throws RecipientFormattingException {
String rawText = recipientsText.getText().toString();
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, true);
if (recipients.isEmpty())
throw new RecipientFormattingException("Recipient List Is Empty!");
return recipients;
}
public void disable() {
clear();
panel.setVisibility(View.GONE);
}
public void clear() {
recipientsText.setText("");
}
public void setPanelChangeListener(RecipientsPanelChangedListener panelChangeListener) {
this.panelChangeListener = panelChangeListener;
}
private void initialize() {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.single_recipient_panel, this, true);
panel = findViewById(R.id.recipients_panel);
initRecipientsEditor();
}
private void initRecipientsEditor() {
Recipients recipients;
recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text);
try {
recipients = getRecipients();
} catch (RecipientFormattingException e) {
recipients = RecipientFactory.getRecipientsFor(getContext(), new LinkedList<Recipient>(), true);
}
recipients.addListener(this);
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
recipientsText.populate(recipients);
recipientsText.setOnFocusChangeListener(new FocusChangedListener());
recipientsText.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (panelChangeListener != null) {
try {
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
} catch (RecipientFormattingException rfe) {
panelChangeListener.onRecipientsPanelUpdate(null);
}
}
recipientsText.setText("");
}
});
}
@Override public void onModified(Recipients recipients) {
recipientsText.populate(recipients);
}
private class FocusChangedListener implements OnFocusChangeListener {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && (panelChangeListener != null)) {
try {
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
} catch (RecipientFormattingException rfe) {
panelChangeListener.onRecipientsPanelUpdate(null);
}
}
}
}
public interface RecipientsPanelChangedListener {
public void onRecipientsPanelUpdate(Recipients recipients);
}
}

View File

@ -46,7 +46,7 @@ public class UntrustedSendDialog extends AlertDialog.Builder implements DialogIn
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setApproval(identityRecord.getRecipientId(), true);
identityDatabase.setApproval(identityRecord.getAddress(), true);
}
}

View File

@ -45,7 +45,7 @@ public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogI
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setVerified(identityRecord.getRecipientId(),
identityDatabase.setVerified(identityRecord.getAddress(),
identityRecord.getIdentityKey(),
IdentityDatabase.VerifiedStatus.DEFAULT);
}

View File

@ -120,7 +120,7 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient
String introduction = String.format(getContext().getString(R.string.WebRtcCallScreen_new_safety_numbers), name, name);
SpannableString spannableString = new SpannableString(introduction + " " + getContext().getString(R.string.WebRtcCallScreen_you_may_wish_to_verify_this_contact));
spannableString.setSpan(new VerifySpan(getContext(), personInfo.getRecipientId(), untrustedIdentity),
spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity),
introduction.length()+1, spannableString.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@ -302,7 +302,7 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient
}.execute();
this.name.setText(recipient.getName());
this.phoneNumber.setText(recipient.getNumber());
this.phoneNumber.setText(recipient.getAddress().serialize());
}
private void setCard(Recipient recipient, String status) {

View File

@ -10,9 +10,10 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactSelectionListItem extends LinearLayout implements Recipients.RecipientsModifiedListener {
@ -53,9 +54,10 @@ public class ContactSelectionListItem extends LinearLayout implements Recipients
if (type == ContactsDatabase.NEW_TYPE) {
this.recipients = null;
this.contactPhotoImage.setAvatar(Recipient.getUnknownRecipient(), false);
this.contactPhotoImage.setAvatar(RecipientFactory.getRecipientFor(getContext(), Address.UNKNOWN, true), false);
} else if (!TextUtils.isEmpty(number)) {
this.recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true);
Address address = Address.fromExternal(getContext(), number);
this.recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[] {address}, true);
if (this.recipients.getPrimaryRecipient() != null &&
this.recipients.getPrimaryRecipient().getName() != null)

View File

@ -27,6 +27,7 @@ import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
@ -102,8 +103,8 @@ public class ContactsCursorLoader extends CursorLoader {
ContactsDatabase.LABEL_COLUMN,
ContactsDatabase.CONTACT_TYPE_COLUMN});
while (cursor.moveToNext()) {
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
final Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true);
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
final Recipients recipients = RecipientFactory.getRecipientsFor(getContext(), new Address[]{Address.fromExternal(getContext(), number)}, true);
if (DirectoryHelper.getUserCapabilities(getContext(), recipients)
.getTextCapability() != Capability.SUPPORTED)

View File

@ -128,9 +128,9 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
return mTokenizer.getNumbers();
}
public Recipients constructContactsFromInput() {
return RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false);
}
// public Recipients constructContactsFromInput() {
// return RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false);
// }
private boolean isValidAddress(String number, boolean isMms) {
/*if (isMms) {
@ -191,7 +191,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
public static CharSequence contactToToken(Recipient c) {
String name = c.getName();
String number = c.getNumber();
String number = c.getAddress().serialize();
SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number));
int len = s.length();
@ -199,7 +199,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
return s;
}
s.setSpan(new Annotation("number", c.getNumber()), 0, len,
s.setSpan(new Annotation("number", c.getAddress().serialize()), 0, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return s;

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.state.SessionRecord;
@ -15,12 +16,12 @@ import java.util.List;
public class SessionUtil {
public static boolean hasSession(Context context, MasterSecret masterSecret, Recipient recipient) {
return hasSession(context, masterSecret, recipient.getNumber());
return hasSession(context, masterSecret, recipient.getAddress());
}
public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull String number) {
public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull Address address) {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(number, SignalServiceAddress.DEFAULT_DEVICE_ID);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID);
return sessionStore.containsSession(axolotlAddress);
}

View File

@ -5,12 +5,12 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
@ -47,13 +47,12 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address.getName(), true);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipientId);
Address signalAddress = Address.fromExternal(context, address.getName());
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(signalAddress);
if (!identityRecord.isPresent()) {
Log.w(TAG, "Saving new identity...");
identityDatabase.saveIdentity(recipientId, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
return false;
}
@ -69,15 +68,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
verifiedStatus = VerifiedStatus.DEFAULT;
}
identityDatabase.saveIdentity(recipientId, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, recipients.getPrimaryRecipient());
identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, RecipientFactory.getRecipientFor(context, signalAddress, true));
SessionUtil.archiveSiblingSessions(context, address);
return true;
}
if (isNonBlockingApprovalRequired(identityRecord.get())) {
Log.w(TAG, "Setting approval status...");
identityDatabase.setApproval(recipientId, nonBlockingApproval);
identityDatabase.setApproval(signalAddress, nonBlockingApproval);
return false;
}
@ -94,16 +93,15 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
synchronized (LOCK) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
long recipientId = RecipientFactory.getRecipientsFromString(context, address.getName(), true).getPrimaryRecipient().getRecipientId();
String ourNumber = TextSecurePreferences.getLocalNumber(context);
long ourRecipientId = RecipientFactory.getRecipientsFromString(context, ourNumber, true).getPrimaryRecipient().getRecipientId();
Address theirAddress = Address.fromExternal(context, address.getName());
if (ourRecipientId == recipientId || ourNumber.equals(address.getName())) {
if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) {
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
}
switch (direction) {
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(recipientId));
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress));
case RECEIVING: return true;
default: throw new AssertionError("Unknown direction: " + direction);
}

View File

@ -7,11 +7,9 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
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.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SessionState;
@ -135,7 +133,6 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public List<Integer> getSubDeviceSessions(String name) {
long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
List<Integer> results = new LinkedList<>();
File parent = getSessionDirectory();
String[] children = parent.list();
@ -144,10 +141,10 @@ public class TextSecureSessionStore implements SessionStore {
for (String child : children) {
try {
String[] parts = child.split("[.]", 2);
long sessionRecipientId = Long.parseLong(parts[0]);
String[] parts = child.split("[.]", 2);
String sessionName = parts[0];
if (sessionRecipientId == recipientId && parts.length > 1) {
if (sessionName.equals(name) && parts.length > 1) {
results.add(Integer.parseInt(parts[1]));
}
} catch (NumberFormatException e) {
@ -209,26 +206,21 @@ public class TextSecureSessionStore implements SessionStore {
return directory;
}
private String getSessionName(SignalProtocolAddress axolotlAddress) {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, axolotlAddress.getName(), true)
.getPrimaryRecipient();
long recipientId = recipient.getRecipientId();
int deviceId = axolotlAddress.getDeviceId();
return recipientId + (deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
private String getSessionName(SignalProtocolAddress address) {
int deviceId = address.getDeviceId();
return address.getName() + (deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
}
private @Nullable SignalProtocolAddress getAddressName(File sessionFile) {
try {
String[] parts = sessionFile.getName().split("[.]");
Recipient recipient = RecipientFactory.getRecipientForId(context, Integer.valueOf(parts[0]), true);
String[] parts = sessionFile.getName().split("[.]");
int deviceId;
if (parts.length > 1) deviceId = Integer.parseInt(parts[1]);
else deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
return new SignalProtocolAddress(recipient.getNumber(), deviceId);
return new SignalProtocolAddress(parts[0], deviceId);
} catch (NumberFormatException e) {
Log.w(TAG, e);
return null;

View File

@ -0,0 +1,156 @@
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.ShortCodeUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.util.LinkedList;
import java.util.List;
public class Address implements Parcelable, Comparable<Address> {
public static final Parcelable.Creator<Address> CREATOR = new Parcelable.Creator<Address>() {
public Address createFromParcel(Parcel in) {
return new Address(in);
}
public Address[] newArray(int size) {
return new Address[size];
}
};
public static final Address UNKNOWN = new Address("Unknown");
private static final String TAG = Address.class.getSimpleName();
private final String address;
private Address(@NonNull String address) {
if (address == null) throw new AssertionError(address);
this.address = address;
}
public Address(Parcel in) {
this(in.readString());
}
public static Address fromSerialized(@NonNull String serialized) {
return new Address(serialized);
}
public static List<Address> fromSerializedList(@NonNull String serialized, @NonNull String delimiter) {
List<String> elements = Util.split(serialized, delimiter);
List<Address> addresses = new LinkedList<>();
for (String element : elements) {
addresses.add(Address.fromSerialized(element));
}
return addresses;
}
public static Address fromExternal(@NonNull Context context, @Nullable String external)
{
if (external == null) return new Address("Unknown");
try {
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (GroupUtil.isEncodedGroup(external)) return new Address(external);
else if (NumberUtil.isValidEmail(external)) return new Address(external);
else if (ShortCodeUtil.isShortCode(localNumber, external)) return new Address(external.replaceAll("[^0-9+]", ""));
return new Address(PhoneNumberFormatter.formatNumber(external, localNumber));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
if (TextUtils.isEmpty(external.trim())) return new Address("Unknown");
else return new Address(external.trim());
}
}
public static Address[] fromParcelable(Parcelable[] parcelables) {
Address[] addresses = new Address[parcelables.length];
for (int i=0;i<parcelables.length;i++) {
addresses[i] = (Address)parcelables[i];
}
return addresses;
}
public boolean isGroup() {
return GroupUtil.isEncodedGroup(address);
}
public boolean isEmail() {
return NumberUtil.isValidEmail(address);
}
public boolean isPhone() {
return !isGroup() && !isEmail();
}
public @NonNull String toGroupString() {
if (!isGroup()) throw new AssertionError("Not group: " + address);
return address;
}
public @NonNull String toPhoneString() {
if (!isPhone()) throw new AssertionError("Not e164: " + address);
return address;
}
public @NonNull String toEmailString() {
if (!isEmail()) throw new AssertionError("Not email: " + address);
return address;
}
@Override
public String toString() {
return address;
}
public String serialize() {
return address;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || !(other instanceof Address)) return false;
return address.equals(((Address) other).address);
}
@Override
public int hashCode() {
return address.hashCode();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(address);
}
@Override
public int compareTo(@NonNull Address other) {
return address.compareTo(other.address);
}
}

View File

@ -1,261 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ShortCodeUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CanonicalAddressDatabase {
private static final String TAG = CanonicalAddressDatabase.class.getSimpleName();
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "canonical_address.db";
private static final String TABLE = "canonical_addresses";
private static final String ID_COLUMN = "_id";
private static final String ADDRESS_COLUMN = "address";
private static final String DATABASE_CREATE = "CREATE TABLE " + TABLE + " (" + ID_COLUMN + " integer PRIMARY KEY, " + ADDRESS_COLUMN + " TEXT NOT NULL);";
private static final String SELECTION_NUMBER = "PHONE_NUMBERS_EQUAL(" + ADDRESS_COLUMN + ", ?)";
private static final String SELECTION_OTHER = ADDRESS_COLUMN + " = ? COLLATE NOCASE";
private static CanonicalAddressDatabase instance;
private DatabaseHelper databaseHelper;
private final Context context;
private final Map<String, Long> addressCache = new ConcurrentHashMap<>();
private final Map<Long, String> idCache = new ConcurrentHashMap<>();
private final Map<String, String> formattedAddressCache = Collections.synchronizedMap(new LRUCache<String, String>(100));
public synchronized static CanonicalAddressDatabase getInstance(Context context) {
if (instance == null)
instance = new CanonicalAddressDatabase(context.getApplicationContext());
return instance;
}
private CanonicalAddressDatabase(Context context) {
this.context = context;
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
fillCache();
}
public void reset(Context context) {
DatabaseHelper old = this.databaseHelper;
this.databaseHelper = new DatabaseHelper(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
old.close();
fillCache();
}
private void fillCache() {
Cursor cursor = null;
try {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
cursor = db.query(TABLE, null, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN));
if (address == null || address.trim().length() == 0)
address = "Anonymous";
idCache.put(id, address);
addressCache.put(address, id);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public @NonNull String getAddressFromId(long id) {
String cachedAddress = idCache.get(id);
if (cachedAddress != null)
return cachedAddress;
Cursor cursor = null;
try {
Log.w(TAG, "Hitting DB on query [ID].");
SQLiteDatabase db = databaseHelper.getReadableDatabase();
cursor = db.query(TABLE, null, ID_COLUMN + " = ?", new String[] {id+""}, null, null, null);
if (!cursor.moveToFirst())
return "Anonymous";
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN));
if (address == null || address.trim().equals("")) {
return "Anonymous";
} else {
idCache.put(id, address);
return address;
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void close() {
databaseHelper.close();
instance = null;
}
public long getCanonicalAddressId(@NonNull String address) {
try {
long canonicalAddressId;
String formattedAddress;
if ((formattedAddress = formattedAddressCache.get(address)) == null) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (!isNumberAddress(address) ||
!TextSecurePreferences.isPushRegistered(context) ||
ShortCodeUtil.isShortCode(localNumber, address))
{
formattedAddress = address;
} else {
formattedAddress = PhoneNumberFormatter.formatNumber(address, localNumber);
}
formattedAddressCache.put(address, formattedAddress);
}
if ((canonicalAddressId = getCanonicalAddressFromCache(formattedAddress)) == -1) {
canonicalAddressId = getCanonicalAddressIdFromDatabase(formattedAddress);
}
idCache.put(canonicalAddressId, formattedAddress);
addressCache.put(formattedAddress, canonicalAddressId);
return canonicalAddressId;
} catch (InvalidNumberException e) {
throw new AssertionError(e);
}
}
public @NonNull List<Long> getCanonicalAddressIds(@NonNull List<String> addresses) {
List<Long> addressList = new LinkedList<>();
for (String address : addresses) {
addressList.add(getCanonicalAddressId(address));
}
return addressList;
}
private long getCanonicalAddressFromCache(String address) {
Long cachedAddress = addressCache.get(address);
return cachedAddress == null ? -1L : cachedAddress;
}
private long getCanonicalAddressIdFromDatabase(@NonNull String address) {
Log.w(TAG, "Hitting DB on query [ADDRESS]");
Cursor cursor = null;
try {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String[] selectionArguments = new String[]{address};
boolean isNumber = isNumberAddress(address);
cursor = db.query(TABLE, null, isNumber ? SELECTION_NUMBER : SELECTION_OTHER,
selectionArguments, null, null, null);
if (cursor.getCount() == 0 || !cursor.moveToFirst()) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(ADDRESS_COLUMN, address);
return db.insert(TABLE, ADDRESS_COLUMN, contentValues);
} else {
long canonicalId = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN));
String oldAddress = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN));
if (!address.equals(oldAddress)) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(ADDRESS_COLUMN, address);
db.update(TABLE, contentValues, ID_COLUMN + " = ?", new String[]{canonicalId+""});
addressCache.remove(oldAddress);
}
return canonicalId;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
@VisibleForTesting
static boolean isNumberAddress(@NonNull String number) {
if (number.contains("@")) return false;
if (GroupUtil.isEncodedGroup(number)) return false;
final String networkNumber = PhoneNumberUtils.extractNetworkPortion(number);
if (TextUtils.isEmpty(networkNumber)) return false;
if (networkNumber.length() < 3) return false;
return PhoneNumberUtils.isWellFormedSmsAddress(number);
}
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}

View File

@ -1,70 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.util.Log;
import java.io.File;
public class CanonicalSessionMigrator {
private static void migrateSession(File sessionFile, File sessionsDirectory, long canonicalAddress) {
File canonicalSessionFile = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress);
sessionFile.renameTo(canonicalSessionFile);
Log.w("CanonicalSessionMigrator", "Moving: " + sessionFile.toString() + " to " + canonicalSessionFile.toString());
File canonicalSessionFileLocal = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress + "-local");
File localFile = new File(sessionFile.getAbsolutePath() + "-local");
if (localFile.exists())
localFile.renameTo(canonicalSessionFileLocal);
Log.w("CanonicalSessionMigrator", "Moving " + localFile + " to " + canonicalSessionFileLocal);
File canonicalSessionFileRemote = new File(sessionsDirectory.getAbsolutePath() + File.separatorChar + canonicalAddress + "-remote");
File remoteFile = new File(sessionFile.getAbsolutePath() + "-remote");
if (remoteFile.exists())
remoteFile.renameTo(canonicalSessionFileRemote);
Log.w("CanonicalSessionMigrator", "Moving " + remoteFile + " to " + canonicalSessionFileRemote);
}
public static void migrateSessions(Context context) {
if (context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).getBoolean("canonicalized", false))
return;
CanonicalAddressDatabase canonicalDb = CanonicalAddressDatabase.getInstance(context);
File rootDirectory = context.getFilesDir();
File sessionsDirectory = new File(rootDirectory.getAbsolutePath() + File.separatorChar + "sessions");
sessionsDirectory.mkdir();
String[] files = rootDirectory.list();
for (int i=0;i<files.length;i++) {
File item = new File(rootDirectory.getAbsolutePath() + File.separatorChar + files[i]);
if (!item.isDirectory() && files[i].matches("[0-9]+")) {
long canonicalAddress = canonicalDb.getCanonicalAddressId(files[i]);
migrateSession(item, sessionsDirectory, canonicalAddress);
}
}
context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).edit().putBoolean("canonicalized", true).apply();
}
}

View File

@ -22,18 +22,30 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidMessageException;
@ -42,6 +54,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DatabaseFactory {
@ -80,7 +98,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_VOICE_NOTES = 34;
private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35;
private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36;
private static final int DATABASE_VERSION = 36;
private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37;
private static final int DATABASE_VERSION = 37;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@ -95,7 +114,6 @@ public class DatabaseFactory {
private final AttachmentDatabase attachments;
private final MediaDatabase media;
private final ThreadDatabase thread;
private final CanonicalAddressDatabase address;
private final MmsAddressDatabase mmsAddress;
private final MmsSmsDatabase mmsSmsDatabase;
private final IdentityDatabase identityDatabase;
@ -130,10 +148,6 @@ public class DatabaseFactory {
return getInstance(context).mms;
}
public static CanonicalAddressDatabase getAddressDatabase(Context context) {
return getInstance(context).address;
}
public static EncryptingSmsDatabase getEncryptingSmsDatabase(Context context) {
return getInstance(context).encryptingSms;
}
@ -182,7 +196,6 @@ public class DatabaseFactory {
this.attachments = new AttachmentDatabase(context, databaseHelper);
this.media = new MediaDatabase(context, databaseHelper);
this.thread = new ThreadDatabase(context, databaseHelper);
this.address = CanonicalAddressDatabase.getInstance(context);
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
@ -210,8 +223,6 @@ public class DatabaseFactory {
this.groupDatabase.reset(databaseHelper);
this.recipientPreferenceDatabase.reset(databaseHelper);
old.close();
this.address.reset(context);
}
public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion,
@ -504,8 +515,13 @@ public class DatabaseFactory {
private static class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = DatabaseHelper.class.getSimpleName();
private final Context context;
public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
this.context = context.getApplicationContext();
}
@Override
@ -883,6 +899,299 @@ public class DatabaseFactory {
db.execSQL("UPDATE part SET pending_push = '2' WHERE pending_push = '1'");
}
if (oldVersion < NO_MORE_CANONICAL_ADDRESS_DATABASE) {
DatabaseHelper canonicalAddressDatabaseHelper = new DatabaseHelper(context, "canonical_address.db", null, 1);
SQLiteDatabase canonicalAddressDatabase = canonicalAddressDatabaseHelper.getReadableDatabase();
NumberMigrator numberMigrator = new NumberMigrator(TextSecurePreferences.getLocalNumber(context));
// Migrate Thread Database
Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long threadId = cursor.getLong(0);
String recipientIdsList = cursor.getString(1);
String[] recipientIds = recipientIdsList.split(" ");
String[] numbers = new String[recipientIds.length];
for (int i=0;i<recipientIds.length;i++) {
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {recipientIds[i]}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
numbers[i] = numberMigrator.migrate(address);
} else {
throw new AssertionError("Unable to resolve: " + recipientIds[i]);
}
if (resolved != null) resolved.close();
}
ContentValues values = new ContentValues(1);
values.put("recipient_ids", Util.join(numbers, " "));
db.update("thread", values, "_id = ?", new String[] {String.valueOf(threadId)});
}
if (cursor != null) cursor.close();
// Migrate Identity database
db.execSQL("CREATE TABLE identities_migrated (_id INTEGER PRIMARY KEY, address TEXT UNIQUE, key TEXT, first_use INTEGER DEFAULT 0, timestamp INTEGER DEFAULT 0, verified INTEGER DEFAULT 0, nonblocking_approval INTEGER DEFAULT 0);");
cursor = db.query("identities", new String[] {"_id, recipient, key, first_use, timestamp, verified, nonblocking_approval"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
long recipientId = cursor.getLong(1);
String key = cursor.getString(2);
int firstUse = cursor.getInt(3);
long timestamp = cursor.getLong(4);
int verified = cursor.getInt(5);
int nonblockingApproval = cursor.getInt(6);
ContentValues values = new ContentValues(6);
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
values.put("address", numberMigrator.migrate(address));
values.put("key", key);
values.put("first_use", firstUse);
values.put("timestamp", timestamp);
values.put("verified", verified);
values.put("nonblocking_approval", nonblockingApproval);
} else {
throw new AssertionError("Unable to resolve: " + recipientId);
}
if (resolved != null) resolved.close();
db.insert("identities_migrated", null, values);
}
if (cursor != null) cursor.close();
db.execSQL("DROP TABLE identities");
db.execSQL("ALTER TABLE identities_migrated RENAME TO identities");
// Migrate recipient preferences database
cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String recipientIdsList = cursor.getString(1);
String[] recipientIds = recipientIdsList.split(" ");
String[] addresses = new String[recipientIds.length];
for (int i=0;i<recipientIds.length;i++) {
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {recipientIds[i]}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
addresses[i] = numberMigrator.migrate(address);
} else {
throw new AssertionError("Unable to resolve: " + recipientIds[i]);
}
if (resolved != null) resolved.close();
}
ContentValues values = new ContentValues(1);
values.put("recipient_ids", Util.join(addresses, " "));
db.update("thread", values, "_id = ?", new String[] {String.valueOf(id)});
}
if (cursor != null) cursor.close();
// Migrate SMS database
cursor = db.query("sms", new String[] {"_id", "address"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String address = cursor.getString(1);
if (!TextUtils.isEmpty(address)) {
ContentValues values = new ContentValues(1);
values.put("address", numberMigrator.migrate(address));
db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)});
}
}
if (cursor != null) cursor.close();
// Migrate MMS database
cursor = db.query("mms", new String[] {"_id", "address"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String address = cursor.getString(1);
if (!TextUtils.isEmpty(address)) {
ContentValues values = new ContentValues(1);
values.put("address", numberMigrator.migrate(address));
db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)});
}
}
if (cursor != null) cursor.close();
// Migrate MmsAddressDatabase
cursor = db.query("mms_addresses", new String[] {"_id", "address"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String address = cursor.getString(1);
if (!TextUtils.isEmpty(address) && !"insert-address-token".equals(address)) {
ContentValues values = new ContentValues(1);
values.put("address", numberMigrator.migrate(address));
db.update("mms_addresses", values, "_id = ?", new String[] {String.valueOf(id)});
}
}
if (cursor != null) cursor.close();
// Migrate SMS mismatched identities
cursor = db.query("sms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String document = cursor.getString(1);
if (!TextUtils.isEmpty(document)) {
try {
PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class);
List<PostCanonicalAddressIdentityMismatchDocument> newDocumentList = new LinkedList<>();
for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) {
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey));
} else {
throw new AssertionError("Unable to resolve: " + oldDocument.recipientId);
}
if (resolved != null) resolved.close();
}
ContentValues values = new ContentValues(1);
values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList)));
db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)});
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
if (cursor != null) cursor.close();
// Migrate MMS mismatched identities
cursor = db.query("mms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String document = cursor.getString(1);
if (!TextUtils.isEmpty(document)) {
try {
PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class);
List<PostCanonicalAddressIdentityMismatchDocument> newDocumentList = new LinkedList<>();
for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) {
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey));
} else {
throw new AssertionError("Unable to resolve: " + oldDocument.recipientId);
}
if (resolved != null) resolved.close();
}
ContentValues values = new ContentValues(1);
values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList)));
db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)});
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
if (cursor != null) cursor.close();
// Migrate MMS network failures
cursor = db.query("mms", new String[] {"_id", "network_failures"}, "network_failures IS NOT NULL", null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String document = cursor.getString(1);
if (!TextUtils.isEmpty(document)) {
try {
PreCanonicalAddressNetworkFailureList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressNetworkFailureList.class);
List<PostCanonicalAddressNetworkFailureDocument> newDocumentList = new LinkedList<>();
for (PreCanonicalAddressNetworkFailureDocument oldDocument : oldDocumentList.list) {
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null);
if (resolved != null && resolved.moveToFirst()) {
String address = resolved.getString(0);
newDocumentList.add(new PostCanonicalAddressNetworkFailureDocument(numberMigrator.migrate(address)));
} else {
throw new AssertionError("Unable to resolve: " + oldDocument.recipientId);
}
if (resolved != null) resolved.close();
}
ContentValues values = new ContentValues(1);
values.put("network_failures", JsonUtils.toJson(new PostCanonicalAddressNetworkFailureList(newDocumentList)));
db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)});
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
// Migrate sessions
File sessionsDirectory = new File(context.getFilesDir(), "sessions-v2");
if (sessionsDirectory.exists() && sessionsDirectory.isDirectory()) {
File[] sessions = sessionsDirectory.listFiles();
for (File session : sessions) {
try {
String[] sessionParts = session.getName().split("[.]");
long recipientId = Long.parseLong(sessionParts[0]);
int deviceId;
if (sessionParts.length > 1) deviceId = Integer.parseInt(sessionParts[1]);
else deviceId = 1;
Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null);
if (resolved != null && resolved.moveToNext()) {
String address = resolved.getString(0);
File destination = new File(session.getParentFile(), address + (deviceId != 1 ? "." + deviceId : ""));
if (!session.renameTo(destination)) {
Log.w(TAG, "Session rename failed: " + destination);
}
}
if (resolved != null) resolved.close();
} catch (NumberFormatException e) {
Log.w(TAG, e);
}
}
}
}
db.setTransactionSuccessful();
db.endTransaction();
}
@ -893,4 +1202,153 @@ public class DatabaseFactory {
}
}
private static class PreCanonicalAddressIdentityMismatchList {
@JsonProperty(value = "m")
private List<PreCanonicalAddressIdentityMismatchDocument> list;
}
private static class PostCanonicalAddressIdentityMismatchList {
@JsonProperty(value = "m")
private List<PostCanonicalAddressIdentityMismatchDocument> list;
public PostCanonicalAddressIdentityMismatchList(List<PostCanonicalAddressIdentityMismatchDocument> list) {
this.list = list;
}
}
private static class PreCanonicalAddressIdentityMismatchDocument {
@JsonProperty(value = "r")
private long recipientId;
@JsonProperty(value = "k")
private String identityKey;
}
private static class PostCanonicalAddressIdentityMismatchDocument {
@JsonProperty(value = "a")
private String address;
@JsonProperty(value = "k")
private String identityKey;
public PostCanonicalAddressIdentityMismatchDocument() {}
public PostCanonicalAddressIdentityMismatchDocument(String address, String identityKey) {
this.address = address;
this.identityKey = identityKey;
}
}
private static class PreCanonicalAddressNetworkFailureList {
@JsonProperty(value = "l")
private List<PreCanonicalAddressNetworkFailureDocument> list;
}
private static class PostCanonicalAddressNetworkFailureList {
@JsonProperty(value = "l")
private List<PostCanonicalAddressNetworkFailureDocument> list;
public PostCanonicalAddressNetworkFailureList(List<PostCanonicalAddressNetworkFailureDocument> list) {
this.list = list;
}
}
private static class PreCanonicalAddressNetworkFailureDocument {
@JsonProperty(value = "r")
private long recipientId;
}
private static class PostCanonicalAddressNetworkFailureDocument {
@JsonProperty(value = "a")
private String address;
public PostCanonicalAddressNetworkFailureDocument() {}
public PostCanonicalAddressNetworkFailureDocument(String address) {
this.address = address;
}
}
private static class NumberMigrator {
private static final String TAG = NumberMigrator.class.getSimpleName();
private static final Set<String> SHORT_COUNTRIES = new HashSet<String>() {{
add("NU");
add("TK");
add("NC");
add("AC");
}};
private final Phonenumber.PhoneNumber localNumber;
private final String localNumberString;
private final String localCountryCode;
private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
public NumberMigrator(String localNumber) {
try {
this.localNumberString = localNumber;
this.localNumber = phoneNumberUtil.parse(localNumber, null);
this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(this.localNumber);
} catch (NumberParseException e) {
throw new AssertionError(e);
}
}
public String migrate(@Nullable String number) {
if (number == null) return "Unknown";
if (number.startsWith("__textsecure_group__!")) return number;
if (android.util.Patterns.EMAIL_ADDRESS.matcher(number).matches()) return number;
String bareNumber = number.replaceAll("[^0-9+]", "");
if (bareNumber.length() == 0) {
if (TextUtils.isEmpty(number.trim())) return "Unknown";
else return number.trim();
}
// libphonenumber doesn't seem to be correct for Germany and Finland
if (bareNumber.length() <= 6 && ("DE".equals(localCountryCode) || "FI".equals(localCountryCode) || "SK".equals(localCountryCode))) {
return bareNumber;
}
// libphonenumber seems incorrect for Russia and a few other countries with 4 digit short codes.
if (bareNumber.length() <= 4 && !SHORT_COUNTRIES.contains(localCountryCode)) {
return bareNumber;
}
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(bareNumber, localCountryCode);
if (ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(parsedNumber, localCountryCode)) {
return bareNumber;
}
return phoneNumberUtil.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
} catch (NumberParseException e) {
Log.w(TAG, e);
if (bareNumber.charAt(0) == '+')
return bareNumber;
String localNumberImprecise = localNumberString;
if (localNumberImprecise.charAt(0) == '+')
localNumberImprecise = localNumberImprecise.substring(1);
if (localNumberImprecise.length() == number.length() || number.length() > localNumberImprecise.length())
return "+" + number;
int difference = localNumberImprecise.length() - number.length();
return "+" + localNumberImprecise.substring(0, difference) + number;
}
}
}
}

View File

@ -11,7 +11,7 @@ public class EarlyReceiptCache {
private final LRUCache<Placeholder, Long> cache = new LRUCache<>(100);
public synchronized void increment(long timestamp, String address) {
public synchronized void increment(long timestamp, Address address) {
Log.w(TAG, this+"");
Log.w(TAG, String.format("Early receipt: %d,%s", timestamp, address));
Placeholder tuple = new Placeholder(timestamp, address);
@ -24,7 +24,7 @@ public class EarlyReceiptCache {
}
}
public synchronized long remove(long timestamp, String address) {
public synchronized long remove(long timestamp, Address address) {
Long count = cache.remove(new Placeholder(timestamp, address));
Log.w(TAG, this+"");
Log.w(TAG, String.format("Checking early receipts (%d, %s): %d", timestamp, address, count));
@ -34,9 +34,9 @@ public class EarlyReceiptCache {
private class Placeholder {
private final long timestamp;
private final @NonNull String address;
private final @NonNull Address address;
private Placeholder(long timestamp, @NonNull String address) {
private Placeholder(long timestamp, @NonNull Address address) {
this.timestamp = timestamp;
this.address = address;
}

View File

@ -101,22 +101,20 @@ public class GroupDatabase extends Database {
}
public @NonNull Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
List<String> members = getCurrentMembers(groupId);
List<Address> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>();
for (String member : members) {
if (!includeSelf && member.equals(localNumber))
for (Address member : members) {
if (!includeSelf && Util.isOwnNumber(context, member))
continue;
recipients.addAll(RecipientFactory.getRecipientsFromString(context, member, false)
.getRecipientsList());
recipients.add(RecipientFactory.getRecipientFor(context, member, false));
}
return RecipientFactory.getRecipientsFor(context, recipients, false);
}
public void create(byte[] groupId, String title, List<String> members,
public void create(byte[] groupId, String title, List<Address> members,
SignalServiceAttachmentPointer avatar, String relay)
{
ContentValues contentValues = new ContentValues();
@ -185,7 +183,7 @@ public class GroupDatabase extends Database {
notifyDatabaseListeners();
}
public void updateMembers(byte[] id, List<String> members) {
public void updateMembers(byte[] id, List<Address> members) {
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Util.join(members, ","));
contents.put(ACTIVE, 1);
@ -194,8 +192,8 @@ public class GroupDatabase extends Database {
new String[] {GroupUtil.getEncodedId(id)});
}
public void remove(byte[] id, String source) {
List<String> currentMembers = getCurrentMembers(id);
public void remove(byte[] id, Address source) {
List<Address> currentMembers = getCurrentMembers(id);
currentMembers.remove(source);
ContentValues contents = new ContentValues();
@ -205,7 +203,7 @@ public class GroupDatabase extends Database {
new String[] {GroupUtil.getEncodedId(id)});
}
private List<String> getCurrentMembers(byte[] id) {
private List<Address> getCurrentMembers(byte[] id) {
Cursor cursor = null;
try {
@ -215,7 +213,13 @@ public class GroupDatabase extends Database {
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ",");
List<Address> results = new LinkedList<>();
for (String member : Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ",")) {
results.add(Address.fromSerialized(member));
}
return results;
}
return new LinkedList<>();
@ -286,16 +290,16 @@ public class GroupDatabase extends Database {
public static class GroupRecord {
private final String id;
private final String title;
private final List<String> members;
private final byte[] avatar;
private final long avatarId;
private final byte[] avatarKey;
private final byte[] avatarDigest;
private final String avatarContentType;
private final String relay;
private final boolean active;
private final String id;
private final String title;
private final List<Address> members;
private final byte[] avatar;
private final long avatarId;
private final byte[] avatarKey;
private final byte[] avatarDigest;
private final String avatarContentType;
private final String relay;
private final boolean active;
public GroupRecord(String id, String title, String members, byte[] avatar,
long avatarId, byte[] avatarKey, String avatarContentType,
@ -303,7 +307,7 @@ public class GroupDatabase extends Database {
{
this.id = id;
this.title = title;
this.members = Util.split(members, ",");
this.members = Address.fromSerializedList(members, ",");
this.avatar = avatar;
this.avatarId = avatarId;
this.avatarKey = avatarKey;
@ -329,7 +333,7 @@ public class GroupDatabase extends Database {
return title;
}
public List<String> getMembers() {
public List<Address> getMembers() {
return members;
}

View File

@ -38,7 +38,7 @@ public class IdentityDatabase extends Database {
private static final String TABLE_NAME = "identities";
private static final String ID = "_id";
private static final String RECIPIENT = "recipient";
private static final String ADDRESS = "address";
private static final String IDENTITY_KEY = "key";
private static final String TIMESTAMP = "timestamp";
private static final String FIRST_USE = "first_use";
@ -47,7 +47,7 @@ public class IdentityDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
RECIPIENT + " INTEGER UNIQUE, " +
ADDRESS + " TEXT UNIQUE, " +
IDENTITY_KEY + " TEXT, " +
FIRST_USE + " INTEGER DEFAULT 0, " +
TIMESTAMP + " INTEGER DEFAULT 0, " +
@ -65,8 +65,8 @@ public class IdentityDatabase extends Database {
}
public static VerifiedStatus forState(int state) {
if (state == 0) return DEFAULT;
else if (state == 1) return VERIFIED;
if (state == 0) return DEFAULT;
else if (state == 1) return VERIFIED;
else if (state == 2) return UNVERIFIED;
else throw new AssertionError("No such state: " + state);
}
@ -86,13 +86,13 @@ public class IdentityDatabase extends Database {
return new IdentityReader(cursor);
}
public Optional<IdentityRecord> getIdentity(long recipientId) {
public Optional<IdentityRecord> getIdentity(Address address) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, RECIPIENT + " = ?",
new String[] {recipientId + ""}, null, null, null);
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?",
new String[] {address.serialize()}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return Optional.of(getIdentityRecord(cursor));
@ -106,14 +106,14 @@ public class IdentityDatabase extends Database {
return Optional.absent();
}
public void saveIdentity(long recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus,
public void saveIdentity(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus,
boolean firstUse, long timestamp, boolean nonBlockingApproval)
{
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String identityKeyString = Base64.encodeBytes(identityKey.serialize());
ContentValues contentValues = new ContentValues();
contentValues.put(RECIPIENT, recipientId);
contentValues.put(ADDRESS, address.serialize());
contentValues.put(IDENTITY_KEY, identityKeyString);
contentValues.put(TIMESTAMP, timestamp);
contentValues.put(VERIFIED, verifiedStatus.toInt());
@ -122,38 +122,36 @@ public class IdentityDatabase extends Database {
database.replace(TABLE_NAME, null, contentValues);
EventBus.getDefault().post(new IdentityRecord(recipientId, identityKey, verifiedStatus,
EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus,
firstUse, timestamp, nonBlockingApproval));
}
public void setApproval(long recipientId, boolean nonBlockingApproval) {
public void setApproval(Address address, boolean nonBlockingApproval) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(2);
contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval);
database.update(TABLE_NAME, contentValues, RECIPIENT + " = ?",
new String[] {String.valueOf(recipientId)});
database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()});
}
public void setVerified(long recipientId, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
public void setVerified(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
contentValues.put(VERIFIED, verifiedStatus.toInt());
int updated = database.update(TABLE_NAME, contentValues, RECIPIENT + " = ? AND " + IDENTITY_KEY + " = ?",
new String[] {String.valueOf(recipientId),
Base64.encodeBytes(identityKey.serialize())});
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?",
new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())});
if (updated > 0) {
Optional<IdentityRecord> record = getIdentity(recipientId);
Optional<IdentityRecord> record = getIdentity(address);
if (record.isPresent()) EventBus.getDefault().post(record.get());
}
}
private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException {
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED));
@ -161,23 +159,23 @@ public class IdentityDatabase extends Database {
boolean firstUse = cursor.getInt(cursor.getColumnIndexOrThrow(FIRST_USE)) == 1;
IdentityKey identity = new IdentityKey(Base64.decode(serializedIdentity), 0);
return new IdentityRecord(recipientId, identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval);
return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval);
}
public static class IdentityRecord {
private final long recipientId;
private final Address address;
private final IdentityKey identitykey;
private final VerifiedStatus verifiedStatus;
private final boolean firstUse;
private final long timestamp;
private final boolean nonblockingApproval;
private IdentityRecord(long recipientId,
private IdentityRecord(Address address,
IdentityKey identitykey, VerifiedStatus verifiedStatus,
boolean firstUse, long timestamp, boolean nonblockingApproval)
{
this.recipientId = recipientId;
this.address = address;
this.identitykey = identitykey;
this.verifiedStatus = verifiedStatus;
this.firstUse = firstUse;
@ -185,8 +183,8 @@ public class IdentityDatabase extends Database {
this.nonblockingApproval = nonblockingApproval;
}
public long getRecipientId() {
return recipientId;
public Address getAddress() {
return address;
}
public IdentityKey getIdentityKey() {
@ -211,7 +209,7 @@ public class IdentityDatabase extends Database {
@Override
public String toString() {
return "{recipientId: " + recipientId + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}";
return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}";
}
}

View File

@ -5,9 +5,9 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -57,10 +57,10 @@ public class MediaDatabase extends Database {
public static class MediaRecord {
private final DatabaseAttachment attachment;
private final String address;
private final Address address;
private final long date;
private MediaRecord(DatabaseAttachment attachment, String address, long date) {
private MediaRecord(DatabaseAttachment attachment, @Nullable Address address, long date) {
this.attachment = attachment;
this.address = address;
this.date = date;
@ -69,7 +69,12 @@ public class MediaDatabase extends Database {
public static MediaRecord from(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Cursor cursor) {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment attachment = attachmentDatabase.getAttachment(masterSecret, cursor);
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
Address address = null;
if (serializedAddress != null) {
address = Address.fromSerialized(serializedAddress);
}
long date;
@ -90,7 +95,7 @@ public class MediaDatabase extends Database {
return attachment.getContentType();
}
public String getAddress() {
public @Nullable Address getAddress() {
return address;
}

View File

@ -17,7 +17,6 @@ import org.whispersystems.libsignal.IdentityKey;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public abstract class MessagingDatabase extends Database implements MmsSmsColumns {
@ -30,9 +29,9 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
protected abstract String getTableName();
public void setMismatchedIdentity(long messageId, final long recipientId, final IdentityKey identityKey) {
public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) {
List<IdentityKeyMismatch> items = new ArrayList<IdentityKeyMismatch>() {{
add(new IdentityKeyMismatch(recipientId, identityKey));
add(new IdentityKeyMismatch(address, identityKey));
}};
IdentityKeyMismatchList document = new IdentityKeyMismatchList(items);
@ -51,20 +50,20 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
}
}
public void addMismatchedIdentity(long messageId, long recipientId, IdentityKey identityKey) {
public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
try {
addToDocument(messageId, MISMATCHED_IDENTITIES,
new IdentityKeyMismatch(recipientId, identityKey),
new IdentityKeyMismatch(address, identityKey),
IdentityKeyMismatchList.class);
} catch (IOException e) {
Log.w(TAG, e);
}
}
public void removeMismatchedIdentity(long messageId, long recipientId, IdentityKey identityKey) {
public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
try {
removeFromDocument(messageId, MISMATCHED_IDENTITIES,
new IdentityKeyMismatch(recipientId, identityKey),
new IdentityKeyMismatch(address, identityKey),
IdentityKeyMismatchList.class);
} catch (IOException e) {
Log.w(TAG, e);
@ -168,15 +167,15 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
public static class SyncMessageId {
private final String address;
private final Address address;
private final long timetamp;
public SyncMessageId(String address, long timetamp) {
public SyncMessageId(Address address, long timetamp) {
this.address = address;
this.timetamp = timetamp;
}
public String getAddress() {
public Address getAddress() {
return address;
}

View File

@ -55,18 +55,18 @@ public class MmsAddressDatabase extends Database {
super(context, databaseHelper);
}
private void insertAddress(long messageId, int type, @NonNull String value) {
private void insertAddress(long messageId, int type, @NonNull Address value) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(MMS_ID, messageId);
contentValues.put(TYPE, type);
contentValues.put(ADDRESS, value);
contentValues.put(ADDRESS, value.serialize());
contentValues.put(ADDRESS_CHARSET, "UTF-8");
database.insert(TABLE_NAME, null, contentValues);
}
private void insertAddress(long messageId, int type, @NonNull List<String> addresses) {
for (String address : addresses) {
private void insertAddress(long messageId, int type, @NonNull List<Address> addresses) {
for (Address address : addresses) {
insertAddress(messageId, type, address);
}
}
@ -84,17 +84,17 @@ public class MmsAddressDatabase extends Database {
public MmsAddresses getAddressesForId(long messageId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
String from = null;
List<String> to = new LinkedList<>();
List<String> cc = new LinkedList<>();
List<String> bcc = new LinkedList<>();
Address from = null;
List<Address> to = new LinkedList<>();
List<Address> cc = new LinkedList<>();
List<Address> bcc = new LinkedList<>();
try {
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {messageId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (type == PduHeaders.FROM) from = address;
if (type == PduHeaders.TO) to.add(address);
@ -109,8 +109,8 @@ public class MmsAddressDatabase extends Database {
return new MmsAddresses(from, to, cc, bcc);
}
public List<String> getAddressesListForId(long messageId) {
List<String> results = new LinkedList<>();
public List<Address> getAddressesListForId(long messageId) {
List<Address> results = new LinkedList<>();
MmsAddresses addresses = getAddressesForId(messageId);
if (addresses.getFrom() != null) {
@ -125,13 +125,12 @@ public class MmsAddressDatabase extends Database {
}
public Recipients getRecipientsForId(long messageId) {
List<String> numbers = getAddressesListForId(messageId);
List<Recipient> results = new LinkedList<>();
List<Address> addresses = getAddressesListForId(messageId);
List<Recipient> results = new LinkedList<>();
for (String number : numbers) {
if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(number)) {
results.add(RecipientFactory.getRecipientsFromString(context, number, false)
.getPrimaryRecipient());
for (Address address : addresses) {
if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(address.serialize())) {
results.add(RecipientFactory.getRecipientFor(context, address, false));
}
}

View File

@ -8,13 +8,13 @@ import java.util.List;
public class MmsAddresses {
private final @Nullable String from;
private final @NonNull List<String> to;
private final @NonNull List<String> cc;
private final @NonNull List<String> bcc;
private final @Nullable Address from;
private final @NonNull List<Address> to;
private final @NonNull List<Address> cc;
private final @NonNull List<Address> bcc;
public MmsAddresses(@Nullable String from, @NonNull List<String> to,
@NonNull List<String> cc, @NonNull List<String> bcc)
public MmsAddresses(@Nullable Address from, @NonNull List<Address> to,
@NonNull List<Address> cc, @NonNull List<Address> bcc)
{
this.from = from;
this.to = to;
@ -23,34 +23,34 @@ public class MmsAddresses {
}
@NonNull
public List<String> getTo() {
public List<Address> getTo() {
return to;
}
@NonNull
public List<String> getCc() {
public List<Address> getCc() {
return cc;
}
@NonNull
public List<String> getBcc() {
public List<Address> getBcc() {
return bcc;
}
@Nullable
public String getFrom() {
public Address getFrom() {
return from;
}
public static MmsAddresses forTo(@NonNull List<String> to) {
return new MmsAddresses(null, to, new LinkedList<String>(), new LinkedList<String>());
public static MmsAddresses forTo(@NonNull List<Address> to) {
return new MmsAddresses(null, to, new LinkedList<Address>(), new LinkedList<Address>());
}
public static MmsAddresses forBcc(@NonNull List<String> bcc) {
return new MmsAddresses(null, new LinkedList<String>(), new LinkedList<String>(), bcc);
public static MmsAddresses forBcc(@NonNull List<Address> bcc) {
return new MmsAddresses(null, new LinkedList<Address>(), new LinkedList<Address>(), bcc);
}
public static MmsAddresses forFrom(@NonNull String from) {
return new MmsAddresses(from, new LinkedList<String>(), new LinkedList<String>(), new LinkedList<String>());
public static MmsAddresses forFrom(@NonNull Address from) {
return new MmsAddresses(from, new LinkedList<Address>(), new LinkedList<Address>(), new LinkedList<Address>());
}
}

View File

@ -51,16 +51,15 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -68,7 +67,6 @@ import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
@ -78,11 +76,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.util.Util.canonicalizeNumber;
import static org.thoughtcrime.securesms.util.Util.canonicalizeNumberOrGroup;
public class MmsDatabase extends MessagingDatabase {
private static final String TAG = MmsDatabase.class.getSimpleName();
@ -212,39 +205,30 @@ public class MmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
List<String> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
for (String storedAddress : addresses) {
try {
String ourAddress = canonicalizeNumber(context, messageId.getAddress());
String theirAddress = canonicalizeNumberOrGroup(context, storedAddress);
for (Address theirAddress : addresses) {
Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
found = true;
found = true;
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
} catch (InvalidNumberException e) {
Log.w("MmsDatabase", e);
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
}
}
}
if (!found) {
try {
earlyReceiptCache.increment(messageId.getTimetamp(), canonicalizeNumber(context, messageId.getAddress()));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
earlyReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
}
} finally {
if (cursor != null)
@ -273,12 +257,12 @@ public class MmsDatabase extends MessagingDatabase {
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException {
if (retrieved.getGroupId() != null) {
Recipients groupRecipients = RecipientFactory.getRecipientsFromString(context, retrieved.getGroupId(), true);
Recipients groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {retrieved.getGroupId()}, true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
}
String localNumber;
Set<String> group = new HashSet<>();
String localNumber;
Set<Address> group = new HashSet<>();
if (retrieved.getAddresses().getFrom() == null) {
throw new MmsException("FROM value in PduHeaders did not exist.");
@ -292,11 +276,11 @@ public class MmsDatabase extends MessagingDatabase {
localNumber = ServiceUtil.getTelephonyManager(context).getLine1Number();
}
for (String cc : retrieved.getAddresses().getCc()) {
for (Address cc : retrieved.getAddresses().getCc()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc);
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc.toPhoneString());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
@ -307,11 +291,11 @@ public class MmsDatabase extends MessagingDatabase {
if (retrieved.getAddresses().getTo().size() > 1) {
for (String to : retrieved.getAddresses().getTo()) {
for (Address to : retrieved.getAddresses().getTo()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to);
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to.toPhoneString());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
@ -322,8 +306,7 @@ public class MmsDatabase extends MessagingDatabase {
}
}
String recipientsList = Util.join(group, ",");
Recipients recipients = RecipientFactory.getRecipientsFromString(context, recipientsList, false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, group.toArray(new Address[0]), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
}
@ -332,8 +315,7 @@ public class MmsDatabase extends MessagingDatabase {
String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null
? Util.toIsoString(notification.getFrom().getTextString())
: "";
Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString, false);
if (recipients.isEmpty()) recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, fromString)}, false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
}
@ -486,7 +468,7 @@ public class MmsDatabase extends MessagingDatabase {
while(cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(cursor.getString(1), cursor.getLong(2));
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true);
result.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -516,34 +498,29 @@ public class MmsDatabase extends MessagingDatabase {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
List<String> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
for (String storedAddress : addresses) {
try {
String ourAddress = canonicalizeNumber(context, messageId.getAddress());
String theirAddress = canonicalizeNumberOrGroup(context, storedAddress);
for (Address theirAddress : addresses) {
Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
ContentValues values = new ContentValues();
values.put(READ, 1);
ContentValues values = new ContentValues();
values.put(READ, 1);
if (expiresIn > 0) {
values.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
if (expiresIn > 0) {
values.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
} catch (InvalidNumberException e) {
Log.w("MmsDatabase", e);
database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
}
}
}
@ -630,14 +607,14 @@ public class MmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId));
MmsAddresses addresses = addr.getAddressesForId(messageId);
List<String> destinations = new LinkedList<>();
List<Address> destinations = new LinkedList<>();
String body = getDecryptedBody(masterSecret, messageText, outboxType);
destinations.addAll(addresses.getBcc());
destinations.addAll(addresses.getCc());
destinations.addAll(addresses.getTo());
Recipients recipients = RecipientFactory.getRecipientsFromStrings(context, destinations, false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, destinations.toArray(new Address[0]), false);
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipients, body, attachments, timestamp, 0);
@ -668,7 +645,7 @@ public class MmsDatabase extends MessagingDatabase {
try {
OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId);
ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getNumber());
contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getAddress().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis());
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.ENCRYPTION_SYMMETRIC_BIT);
contentValues.put(THREAD_ID, getThreadIdForMessage(messageId));
@ -697,7 +674,7 @@ public class MmsDatabase extends MessagingDatabase {
}
return insertMediaMessage(new MasterSecretUnion(masterSecret),
MmsAddresses.forTo(request.getRecipients().toNumberStringList(false)),
MmsAddresses.forTo(request.getRecipients().getAddressesList()),
request.getBody(),
attachments,
contentValues,
@ -726,7 +703,7 @@ public class MmsDatabase extends MessagingDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, retrieved.getSentTimeMillis());
contentValues.put(ADDRESS, retrieved.getAddresses().getFrom());
contentValues.put(ADDRESS, retrieved.getAddresses().getFrom().serialize());
contentValues.put(MESSAGE_BOX, mailbox);
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
@ -847,7 +824,7 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = db.insert(TABLE_NAME, null, contentValues);
if (notification.getFrom() != null) {
addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Util.toIsoString(notification.getFrom().getTextString())));
addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString()))));
}
return new Pair<>(messageId, threadId);
@ -887,7 +864,7 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
}
List<String> recipientNumbers = message.getRecipients().toNumberStringList(true);
List<Address> recipientNumbers = message.getRecipients().getAddressesList();
MmsAddresses addresses;
@ -911,12 +888,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(EXPIRES_IN, message.getExpiresIn());
if (message.getRecipients().isSingleRecipient()) {
try {
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(),
canonicalizeNumber(context, message.getRecipients().getPrimaryRecipient().getNumber())));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipients().getPrimaryRecipient().getAddress()));
}
contentValues.remove(ADDRESS);
@ -1021,7 +993,7 @@ public class MmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingMediaMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom(), String.valueOf(threadId)},
new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom().serialize(), String.valueOf(threadId)},
null, null, null, "1");
try {
@ -1249,21 +1221,21 @@ public class MmsDatabase extends MessagingDatabase {
}
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT));
DisplayRecord.Body body = getBody(cursor);
int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT));
DisplayRecord.Body body = getBody(cursor);
int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
Recipients recipients = getRecipientsFor(address);
List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument);
@ -1276,18 +1248,16 @@ public class MmsDatabase extends MessagingDatabase {
networkFailures, subscriptionId, expiresIn, expireStarted);
}
private Recipients getRecipientsFor(String address) {
if (TextUtils.isEmpty(address) || address.equals("insert-address-token")) {
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true);
private Recipients getRecipientsFor(String serialized) {
Address address;
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
address = Address.UNKNOWN;
} else {
address = Address.fromSerialized(serialized);
}
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, true);
if (recipients == null || recipients.isEmpty()) {
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true);
}
return recipients;
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
}
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {

View File

@ -44,7 +44,7 @@ public class PlaintextBackupExporter {
while ((record = reader.getNext()) != null) {
XmlBackup.XmlBackupItem item =
new XmlBackup.XmlBackupItem(0, record.getIndividualRecipient().getNumber(),
new XmlBackup.XmlBackupItem(0, record.getIndividualRecipient().getAddress().serialize(),
record.getIndividualRecipient().getName(),
record.getDateReceived(),
MmsSmsColumns.Types.translateToSystemBaseType(record.getType()),

View File

@ -34,7 +34,7 @@ public class PlaintextBackupImporter {
XmlBackup.XmlBackupItem item;
while ((item = backup.getNext()) != null) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, item.getAddress(), false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, item.getAddress())}, false);
long threadId = threads.getThreadIdFor(recipients);
SQLiteStatement statement = db.createInsertStatement(transaction);

View File

@ -19,7 +19,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
public class RecipientPreferenceDatabase extends Database {
private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName();
@ -27,7 +26,7 @@ public class RecipientPreferenceDatabase extends Database {
private static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id";
private static final String RECIPIENT_IDS = "recipient_ids";
private static final String ADDRESSES = "recipient_ids";
private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate";
@ -58,7 +57,7 @@ public class RecipientPreferenceDatabase extends Database {
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
RECIPIENT_IDS + " TEXT UNIQUE, " +
ADDRESSES + " TEXT UNIQUE, " +
BLOCK + " INTEGER DEFAULT 0," +
NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
@ -75,7 +74,7 @@ public class RecipientPreferenceDatabase extends Database {
public Cursor getBlocked() {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_IDS}, BLOCK + " = 1",
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESSES}, BLOCK + " = 1",
null, null, null, null, null);
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(RECIPIENT_PREFERENCES_URI));
@ -86,15 +85,15 @@ public class RecipientPreferenceDatabase extends Database {
return new BlockedReader(context, cursor);
}
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull long[] recipients) {
Arrays.sort(recipients);
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull Address[] addresses) {
Arrays.sort(addresses);
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, RECIPIENT_IDS + " = ?",
new String[] {Util.join(recipients, " ")},
cursor = database.query(TABLE_NAME, null, ADDRESSES + " = ?",
new String[] {Util.join(addresses, " ")},
null, null, null);
if (cursor != null && cursor.moveToNext()) {
@ -188,11 +187,11 @@ public class RecipientPreferenceDatabase extends Database {
database.beginTransaction();
int updated = database.update(TABLE_NAME, contentValues, RECIPIENT_IDS + " = ?",
new String[] {String.valueOf(recipients.getSortedIdsString())});
int updated = database.update(TABLE_NAME, contentValues, ADDRESSES + " = ?",
new String[] {Util.join(recipients.getAddresses(), " ")});
if (updated < 1) {
contentValues.put(RECIPIENT_IDS, recipients.getSortedIdsString());
contentValues.put(ADDRESSES, Util.join(recipients.getAddresses(), " "));
database.insert(TABLE_NAME, null, contentValues);
}
@ -274,8 +273,15 @@ public class RecipientPreferenceDatabase extends Database {
}
public @NonNull Recipients getCurrent() {
String recipientIds = cursor.getString(cursor.getColumnIndexOrThrow(RECIPIENT_IDS));
return RecipientFactory.getRecipientsForIds(context, recipientIds, false);
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES));
String[] addresses = serialized.split(" ");
Address[] addressList = new Address[addresses.length];
for (int i=0;i<addresses.length;i++) {
addressList[i] = Address.fromSerialized(addresses[i]);
}
return RecipientFactory.getRecipientsFor(context, addressList, false);
}
public @Nullable Recipients getNext() {

View File

@ -23,7 +23,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@ -35,7 +34,6 @@ import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
@ -44,7 +42,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
@ -53,14 +50,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static org.thoughtcrime.securesms.util.Util.canonicalizeNumber;
/**
* Database for storage of SMS messages.
*
* @author Moxie Marlinspike
*/
public class SmsDatabase extends MessagingDatabase {
private static final String TAG = SmsDatabase.class.getSimpleName();
@ -296,34 +290,26 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
try {
String theirAddress = canonicalizeNumber(context, messageId.getAddress());
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address theirAddress = messageId.getAddress();
Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
if (ourAddress.equals(theirAddress)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
database.execSQL("UPDATE " + TABLE_NAME +
" SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " +
ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
database.execSQL("UPDATE " + TABLE_NAME +
" SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " +
ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
foundMessage = true;
}
} catch (InvalidNumberException e) {
Log.w(TAG, e);
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
foundMessage = true;
}
}
}
if (!foundMessage) {
try {
earlyReceiptCache.increment(messageId.getTimetamp(), canonicalizeNumber(context, messageId.getAddress()));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
earlyReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
}
} finally {
@ -345,7 +331,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(cursor.getString(1), cursor.getLong(2));
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false);
results.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -376,31 +362,27 @@ public class SmsDatabase extends MessagingDatabase {
null, null, null, null);
while (cursor.moveToNext()) {
try {
String theirAddress = canonicalizeNumber(context, messageId.getAddress());
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address theirAddress = messageId.getAddress();
Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
if (ourAddress.equals(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
if (expiresIn > 0) {
contentValues.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
if (expiresIn > 0) {
contentValues.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
} catch (InvalidNumberException e) {
Log.w(TAG, e);
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
}
}
} finally {
@ -440,7 +422,7 @@ public class SmsDatabase extends MessagingDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE);
contentValues.put(ADDRESS, record.getIndividualRecipient().getNumber());
contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize());
contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(DATE_SENT, record.getDateSent());
@ -462,24 +444,24 @@ public class SmsDatabase extends MessagingDatabase {
return new Pair<>(newMessageId, record.getThreadId());
}
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull String number) {
return insertCallLog(number, Types.INCOMING_CALL_TYPE, false);
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull Address address) {
return insertCallLog(address, Types.INCOMING_CALL_TYPE, false);
}
public @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull String number) {
return insertCallLog(number, Types.OUTGOING_CALL_TYPE, false);
public @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull Address address) {
return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false);
}
public @NonNull Pair<Long, Long> insertMissedCall(@NonNull String number) {
return insertCallLog(number, Types.MISSED_CALL_TYPE, true);
public @NonNull Pair<Long, Long> insertMissedCall(@NonNull Address address) {
return insertCallLog(address, Types.MISSED_CALL_TYPE, true);
}
private @NonNull Pair<Long, Long> insertCallLog(@NonNull String number, long type, boolean unread) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, true);
private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, number);
values.put(ADDRESS, address.serialize());
values.put(ADDRESS_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
@ -524,21 +506,14 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Recipients recipients;
if (message.getSender() != null) {
recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), true);
} else {
Log.w(TAG, "Sender is null, returning unknown recipient");
recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), false);
}
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, true);
Recipients groupRecipients;
if (message.getGroupId() == null) {
groupRecipients = null;
} else {
groupRecipients = RecipientFactory.getRecipientsFromString(context, message.getGroupId(), true);
groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getGroupId()}, true);
}
boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
@ -551,7 +526,7 @@ public class SmsDatabase extends MessagingDatabase {
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, message.getSender());
values.put(ADDRESS, message.getSender().serialize());
values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId());
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, message.getSentTimestampMillis());
@ -614,10 +589,10 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
String address = message.getRecipients().getPrimaryRecipient().getNumber();
Address address = message.getRecipients().getPrimaryRecipient().getAddress();
ContentValues contentValues = new ContentValues(6);
contentValues.put(ADDRESS, PhoneNumberUtils.formatNumber(address));
contentValues.put(ADDRESS, address.serialize());
contentValues.put(THREAD_ID, threadId);
contentValues.put(BODY, message.getMessageBody());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
@ -626,12 +601,7 @@ public class SmsDatabase extends MessagingDatabase {
contentValues.put(TYPE, type);
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
try {
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, canonicalizeNumber(context, address)));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, address));
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
@ -671,13 +641,13 @@ public class SmsDatabase extends MessagingDatabase {
return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null);
}
public Cursor getEncryptedRogueMessages(Recipient recipient) {
String selection = TYPE + " & " + Types.ENCRYPTION_REMOTE_NO_SESSION_BIT + " != 0" +
" AND PHONE_NUMBERS_EQUAL(" + ADDRESS + ", ?)";
String[] args = {recipient.getNumber()};
SQLiteDatabase db = databaseHelper.getReadableDatabase();
return db.query(TABLE_NAME, MESSAGE_PROJECTION, selection, args, null, null, null);
}
// public Cursor getEncryptedRogueMessages(Recipient recipient) {
// String selection = TYPE + " & " + Types.ENCRYPTION_REMOTE_NO_SESSION_BIT + " != 0" +
// " AND PHONE_NUMBERS_EQUAL(" + ADDRESS + ", ?)";
// String[] args = {recipient.getNumber()};
// SQLiteDatabase db = databaseHelper.getReadableDatabase();
// return db.query(TABLE_NAME, MESSAGE_PROJECTION, selection, args, null, null, null);
// }
public Cursor getExpirationStartedMessages() {
String where = EXPIRE_STARTED + " > 0";
@ -706,7 +676,7 @@ public class SmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingTextMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender(), String.valueOf(threadId)},
new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender().serialize(), String.valueOf(threadId)},
null, null, null, "1");
try {
@ -843,19 +813,19 @@ public class SmsDatabase extends MessagingDatabase {
}
public SmsMessageRecord getCurrent() {
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT));
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
Recipients recipients = getRecipientsFor(address);
@ -869,19 +839,8 @@ public class SmsDatabase extends MessagingDatabase {
expiresIn, expireStarted);
}
private Recipients getRecipientsFor(String address) {
if (address != null) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, true);
if (recipients == null || recipients.isEmpty()) {
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true);
}
return recipients;
} else {
Log.w(TAG, "getRecipientsFor() address is null");
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(), true);
}
private Recipients getRecipientsFor(Address address) {
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
}
private List<IdentityKeyMismatch> getMismatches(String document) {

View File

@ -27,9 +27,10 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
public class SmsMigrator {
@ -137,24 +138,20 @@ public class SmsMigrator {
}
private static Recipients getOurRecipients(Context context, String theirRecipients) {
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
StringBuilder sb = new StringBuilder();
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
List<Address> addressList = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
String theirRecipientId = tokenizer.nextToken();
String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address == null)
continue;
if (sb.length() != 0)
sb.append(',');
sb.append(address);
if (address != null) {
addressList.add(Address.fromExternal(context, address));
}
}
if (sb.length() == 0) return null;
else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true);
if (addressList.isEmpty()) return null;
else return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), true);
}
private static String encrypt(MasterSecret masterSecret, String body)

View File

@ -7,7 +7,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import android.support.annotation.NonNull;
import android.util.Log;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
@ -69,10 +69,9 @@ public class TextSecureDirectory {
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public boolean isSecureTextSupported(String e164number) throws NotInDirectoryException {
if (e164number == null || e164number.length() == 0) {
return false;
}
public boolean isSecureTextSupported(@NonNull Address address) throws NotInDirectoryException {
if (address.isEmail()) return false;
if (address.isGroup()) return true;
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
@ -80,7 +79,7 @@ public class TextSecureDirectory {
try {
cursor = db.query(TABLE_NAME,
new String[]{REGISTERED}, NUMBER + " = ?",
new String[] {e164number}, null, null, null);
new String[] {address.serialize()}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0) == 1;
@ -94,55 +93,55 @@ public class TextSecureDirectory {
}
}
public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException {
if (TextUtils.isEmpty(e164number)) {
return false;
}
// public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException {
// if (TextUtils.isEmpty(e164number)) {
// return false;
// }
//
// SQLiteDatabase db = databaseHelper.getReadableDatabase();
// Cursor cursor = null;
//
// try {
// cursor = db.query(TABLE_NAME,
// new String[]{VOICE}, NUMBER + " = ?",
// new String[] {e164number}, null, null, null);
//
// if (cursor != null && cursor.moveToFirst()) {
// return cursor.getInt(0) == 1;
// } else {
// throw new NotInDirectoryException();
// }
//
// } finally {
// if (cursor != null)
// cursor.close();
// }
// }
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME,
new String[]{VOICE}, NUMBER + " = ?",
new String[] {e164number}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0) == 1;
} else {
throw new NotInDirectoryException();
}
} finally {
if (cursor != null)
cursor.close();
}
}
public boolean isSecureVideoSupported(String e164number) throws NotInDirectoryException {
if (TextUtils.isEmpty(e164number)) {
return false;
}
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME,
new String[]{VIDEO}, NUMBER + " = ?",
new String[] {e164number}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0) == 1;
} else {
throw new NotInDirectoryException();
}
} finally {
if (cursor != null)
cursor.close();
}
}
// public boolean isSecureVideoSupported(String e164number) throws NotInDirectoryException {
// if (TextUtils.isEmpty(e164number)) {
// return false;
// }
//
// SQLiteDatabase db = databaseHelper.getReadableDatabase();
// Cursor cursor = null;
//
// try {
// cursor = db.query(TABLE_NAME,
// new String[]{VIDEO}, NUMBER + " = ?",
// new String[] {e164number}, null, null, null);
//
// if (cursor != null && cursor.moveToFirst()) {
// return cursor.getInt(0) == 1;
// } else {
// throw new NotInDirectoryException();
// }
//
// } finally {
// if (cursor != null)
// cursor.close();
// }
// }
public String getRelay(String e164number) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();

View File

@ -23,6 +23,7 @@ import android.database.MergeCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
@ -30,21 +31,17 @@ import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -57,7 +54,7 @@ public class ThreadDatabase extends Database {
public static final String ID = "_id";
public static final String DATE = "date";
public static final String MESSAGE_COUNT = "message_count";
public static final String RECIPIENT_IDS = "recipient_ids";
public static final String ADDRESSES = "recipient_ids";
public static final String SNIPPET = "snippet";
private static final String SNIPPET_CHARSET = "snippet_cs";
public static final String READ = "read";
@ -73,7 +70,7 @@ public class ThreadDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESSES + " TEXT, " + SNIPPET + " TEXT, " +
SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " +
@ -82,7 +79,7 @@ public class ThreadDatabase extends Database {
LAST_SEEN + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESSES + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
};
@ -90,42 +87,12 @@ public class ThreadDatabase extends Database {
super(context, databaseHelper);
}
private long[] getRecipientIds(Recipients recipients) {
Set<Long> recipientSet = new HashSet<>();
List<Recipient> recipientList = recipients.getRecipientsList();
for (Recipient recipient : recipientList) {
recipientSet.add(recipient.getRecipientId());
}
long[] recipientArray = new long[recipientSet.size()];
int i = 0;
for (Long recipientId : recipientSet) {
recipientArray[i++] = recipientId;
}
Arrays.sort(recipientArray);
return recipientArray;
}
private String getRecipientsAsString(long[] recipientIds) {
StringBuilder sb = new StringBuilder();
for (int i=0;i<recipientIds.length;i++) {
if (i != 0) sb.append(' ');
sb.append(recipientIds[i]);
}
return sb.toString();
}
private long createThreadForRecipients(String recipients, int recipientCount, int distributionType) {
private long createThreadForRecipients(Address[] addresses, int recipientCount, int distributionType) {
ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis();
contentValues.put(DATE, date - date % 1000);
contentValues.put(RECIPIENT_IDS, recipients);
contentValues.put(ADDRESSES, Util.join(addresses, " "));
if (recipientCount > 1)
contentValues.put(TYPE, distributionType);
@ -304,29 +271,24 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public Cursor getFilteredConversationList(List<String> filter) {
public Cursor getFilteredConversationList(List<Address> filter) {
if (filter == null || filter.size() == 0)
return null;
List<Long> rawRecipientIds = DatabaseFactory.getAddressDatabase(context).getCanonicalAddressIds(filter);
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<List<Address>> partitionedAddresses = Util.partition(filter, 900);
List<Cursor> cursors = new LinkedList<>();
if (rawRecipientIds == null || rawRecipientIds.size() == 0)
return null;
for (List<Address> addresses : partitionedAddresses) {
String selection = ADDRESSES + " = ?";
String[] selectionArgs = new String[addresses.size()];
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<List<Long>> partitionedRecipientIds = Util.partition(rawRecipientIds, 900);
List<Cursor> cursors = new LinkedList<>();
for (List<Long> recipientIds : partitionedRecipientIds) {
String selection = RECIPIENT_IDS + " = ?";
String[] selectionArgs = new String[recipientIds.size()];
for (int i=0;i<recipientIds.size()-1;i++)
selection += (" OR " + RECIPIENT_IDS + " = ?");
for (int i=0;i<addresses.size()-1;i++)
selection += (" OR " + ADDRESSES + " = ?");
int i= 0;
for (long id : recipientIds) {
selectionArgs[i++] = String.valueOf(id);
for (Address address : addresses) {
selectionArgs[i++] = address.serialize();
}
cursors.add(db.query(TABLE_NAME, null, selection, selectionArgs, null, null, DATE + " DESC"));
@ -448,11 +410,10 @@ public class ThreadDatabase extends Database {
}
public long getThreadIdIfExistsFor(Recipients recipients) {
long[] recipientIds = getRecipientIds(recipients);
String recipientsList = getRecipientsAsString(recipientIds);
Address[] addresses = recipients.getAddresses();
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = RECIPIENT_IDS + " = ?";
String[] recipientsArg = new String[] {recipientsList};
String where = ADDRESSES + " = ?";
String[] recipientsArg = new String[] {Util.join(addresses, " ")};
Cursor cursor = null;
try {
@ -473,20 +434,20 @@ public class ThreadDatabase extends Database {
}
public long getThreadIdFor(Recipients recipients, int distributionType) {
long[] recipientIds = getRecipientIds(recipients);
String recipientsList = getRecipientsAsString(recipientIds);
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = RECIPIENT_IDS + " = ?";
String[] recipientsArg = new String[] {recipientsList};
Cursor cursor = null;
Address[] addresses = recipients.getAddresses();
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESSES + " = ?";
String[] recipientsArg = new String[]{Util.join(addresses, " ")};
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
if (cursor != null && cursor.moveToFirst())
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
else
return createThreadForRecipients(recipientsList, recipientIds.length, distributionType);
} else {
return createThreadForRecipients(addresses, recipients.getRecipientsList().size(), distributionType);
}
} finally {
if (cursor != null)
cursor.close();
@ -501,8 +462,8 @@ public class ThreadDatabase extends Database {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String recipientIds = cursor.getString(cursor.getColumnIndexOrThrow(RECIPIENT_IDS));
return RecipientFactory.getRecipientsForIds(context, recipientIds, false);
Address[] addresses = getAddressesFromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES)));
return RecipientFactory.getRecipientsFor(context, addresses, false);
}
} finally {
if (cursor != null)
@ -566,6 +527,17 @@ public class ThreadDatabase extends Database {
return thumbnail != null ? thumbnail.getThumbnailUri() : null;
}
private @NonNull Address[] getAddressesFromSerialized(String serializedAddresses) {
String[] serializedAddressParts = serializedAddresses.split(" ");
Address[] addresses = new Address[serializedAddressParts.length];
for (int i=0;i<serializedAddressParts.length;i++) {
addresses[i] = Address.fromSerialized(serializedAddressParts[i]);
}
return addresses;
}
public static interface ProgressListener {
public void onProgress(int complete, int total);
}
@ -599,9 +571,9 @@ public class ThreadDatabase extends Database {
}
public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
String recipientId = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS));
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId, true);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
Address[] addresses = getAddressesFromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESSES)));
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, true);
DisplayRecord.Body body = getPlaintextBody(cursor);
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));

View File

@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@ -22,8 +23,8 @@ public class IdentityKeyMismatch {
private static final String TAG = IdentityKeyMismatch.class.getSimpleName();
@JsonProperty(value = "r")
private long recipientId;
@JsonProperty(value = "a")
private String address;
@JsonProperty(value = "k")
@JsonSerialize(using = IdentityKeySerializer.class)
@ -32,13 +33,13 @@ public class IdentityKeyMismatch {
public IdentityKeyMismatch() {}
public IdentityKeyMismatch(long recipientId, IdentityKey identityKey) {
this.recipientId = recipientId;
public IdentityKeyMismatch(Address address, IdentityKey identityKey) {
this.address = address.serialize();
this.identityKey = identityKey;
}
public long getRecipientId() {
return recipientId;
public Address getAddress() {
return Address.fromSerialized(address);
}
public IdentityKey getIdentityKey() {
@ -52,12 +53,12 @@ public class IdentityKeyMismatch {
}
IdentityKeyMismatch that = (IdentityKeyMismatch)other;
return that.recipientId == this.recipientId && that.identityKey.equals(this.identityKey);
return that.address.equals(this.address) && that.identityKey.equals(this.identityKey);
}
@Override
public int hashCode() {
return (int)recipientId ^ identityKey.hashCode();
return address.hashCode() ^ identityKey.hashCode();
}
private static class IdentityKeySerializer extends JsonSerializer<IdentityKey> {

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.database.documents;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.LinkedList;
@ -25,6 +26,7 @@ public class IdentityKeyMismatchList implements Document<IdentityKeyMismatch> {
}
@Override
@JsonIgnore
public List<IdentityKeyMismatch> getList() {
return mismatches;
}

View File

@ -2,19 +2,21 @@ package org.thoughtcrime.securesms.database.documents;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thoughtcrime.securesms.database.Address;
public class NetworkFailure {
@JsonProperty(value = "r")
private long recipientId;
@JsonProperty(value = "a")
private String address;
public NetworkFailure(long recipientId) {
this.recipientId = recipientId;
public NetworkFailure(Address address) {
this.address = address.serialize();
}
public NetworkFailure() {}
public long getRecipientId() {
return recipientId;
public Address getAddress() {
return Address.fromSerialized(address);
}
@Override
@ -22,11 +24,11 @@ public class NetworkFailure {
if (other == null || !(other instanceof NetworkFailure)) return false;
NetworkFailure that = (NetworkFailure)other;
return this.recipientId == that.recipientId;
return this.address.equals(that.address);
}
@Override
public int hashCode() {
return (int)recipientId;
return address.hashCode();
}
}

View File

@ -77,7 +77,7 @@ public class IdentityRecordList {
for (IdentityRecord identityRecord : identityRecords) {
if (isUntrusted(identityRecord)) {
untrusted.add(RecipientFactory.getRecipientForId(context, identityRecord.getRecipientId(), false));
untrusted.add(RecipientFactory.getRecipientFor(context, identityRecord.getAddress(), false));
}
}
@ -101,7 +101,7 @@ public class IdentityRecordList {
for (IdentityRecord identityRecord : identityRecords) {
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
unverified.add(RecipientFactory.getRecipientForId(context, identityRecord.getRecipientId(), false));
unverified.add(RecipientFactory.getRecipientFor(context, identityRecord.getAddress(), false));
}
}

View File

@ -6,6 +6,7 @@ import android.database.MatrixCursor;
import android.database.MergeCursor;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
@ -41,7 +42,7 @@ public class ConversationListLoader extends AbstractCursorLoader {
if (archivedCount > 0) {
MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
ThreadDatabase.ADDRESSES, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT,
ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN}, 1);
@ -62,6 +63,12 @@ public class ConversationListLoader extends AbstractCursorLoader {
private Cursor getFilteredConversationList(String filter) {
List<String> numbers = ContactAccessor.getInstance().getNumbersForThreadSearchFilter(context, filter);
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(numbers);
List<Address> addresses = new LinkedList<>();
for (String number : numbers) {
addresses.add(Address.fromExternal(context, number));
}
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(addresses);
}
}

View File

@ -88,6 +88,6 @@ public class WebRtcViewModel {
}
public String toString() {
return "[State: " + state + ", recipient: " + recipient.getNumber() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localVideoEnabled + "]";
return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localVideoEnabled + "]";
}
}

View File

@ -11,6 +11,7 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -24,16 +25,17 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class GroupManager {
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull Set<Recipient> members,
@ -41,23 +43,23 @@ public class GroupManager {
@Nullable String name)
throws InvalidNumberException
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] groupId = groupDatabase.allocateGroupId();
final Set<String> memberE164Numbers = getE164Numbers(context, members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] groupId = groupDatabase.allocateGroupId();
final Set<Address> memberAddresses = getMemberAddresses(context, members);
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context));
groupDatabase.create(groupId, name, new LinkedList<>(memberE164Numbers), null, null);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null);
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
}
private static Set<String> getE164Numbers(Context context, Collection<Recipient> recipients)
private static Set<Address> getMemberAddresses(Context context, Collection<Recipient> recipients)
throws InvalidNumberException
{
final Set<String> results = new HashSet<>();
final Set<Address> results = new HashSet<>();
for (Recipient recipient : recipients) {
results.add(Util.canonicalizeNumber(context, recipient.getNumber()));
results.add(recipient.getAddress());
}
return results;
@ -71,33 +73,39 @@ public class GroupManager {
@Nullable String name)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<String> memberE164Numbers = getE164Numbers(context, members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(context, members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberE164Numbers));
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull byte[] groupId,
@NonNull Set<String> e164numbers,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
Attachment avatarAttachment = null;
String groupRecipientId = GroupUtil.getEncodedId(groupId);
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(context, groupRecipientId, false);
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId));
Recipients groupRecipient = RecipientFactory.getRecipientsFor(context, new Address[]{groupAddress}, false);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(e164numbers);
.addAllMembers(numbers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();

View File

@ -10,13 +10,15 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -37,8 +39,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@ -88,9 +88,16 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
SignalServiceAttachment avatar = group.getAvatar().orNull();
SignalServiceAttachment avatar = group.getAvatar().orNull();
List<Address> members = group.getMembers().isPresent() ? new LinkedList<Address>() : null;
database.create(id, group.getName().orNull(), group.getMembers().orNull(),
if (group.getMembers().isPresent()) {
for (String member : group.getMembers().get()) {
members.add(Address.fromExternal(context, member));
}
}
database.create(id, group.getName().orNull(), members,
avatar != null && avatar.isPointer() ? avatar.asPointer() : null,
envelope.getRelay());
@ -108,24 +115,32 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
Set<String> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<String> messageMembers = new HashSet<>(group.getMembers().get());
Set<Address> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<Address> messageMembers = new HashSet<>();
Set<String> addedMembers = new HashSet<>(messageMembers);
for (String messageMember : group.getMembers().get()) {
messageMembers.add(Address.fromExternal(context, messageMember));
}
Set<Address> addedMembers = new HashSet<>(messageMembers);
addedMembers.removeAll(recordMembers);
Set<String> missingMembers = new HashSet<>(recordMembers);
Set<Address> missingMembers = new HashSet<>(recordMembers);
missingMembers.removeAll(messageMembers);
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
if (addedMembers.size() > 0) {
Set<String> unionMembers = new HashSet<>(recordMembers);
Set<Address> unionMembers = new HashSet<>(recordMembers);
unionMembers.addAll(messageMembers);
database.updateMembers(id, new LinkedList<>(unionMembers));
builder.clearMembers().addAllMembers(addedMembers);
builder.clearMembers();
for (Address addedMember : addedMembers) {
builder.addMembers(addedMember.serialize());
}
} else {
builder.clearMembers();
}
@ -171,13 +186,13 @@ public class GroupMessageProcessor {
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
List<String> members = record.getMembers();
List<Address> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT);
if (members.contains(envelope.getSource())) {
database.remove(id, envelope.getSource());
if (members.contains(Address.fromExternal(context, envelope.getSource()))) {
database.remove(id, Address.fromExternal(context, envelope.getSource()));
if (outgoing) database.setActive(id, false);
return storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing);
@ -202,7 +217,8 @@ public class GroupMessageProcessor {
try {
if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId()));
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {addres}, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null);
@ -213,7 +229,7 @@ public class GroupMessageProcessor {
} else {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0);
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0);
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(masterSecret, groupMessage);

View File

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsRadioException;
import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
@ -42,8 +43,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.thoughtcrime.securesms.mms.MmsException;
public class MmsDownloadJob extends MasterSecretJob {
private static final String TAG = MmsDownloadJob.class.getSimpleName();
@ -206,7 +205,7 @@ public class MmsDownloadJob extends MasterSecretJob {
IncomingMediaMessage message = new IncomingMediaMessage(from, to, cc, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false);
IncomingMediaMessage message = new IncomingMediaMessage(context, from, to, cc, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false);
Optional<InsertResult> insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
message, contentLocation, threadId);

View File

@ -10,6 +10,7 @@ import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduParser;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -85,7 +86,7 @@ public class MmsReceiveJob extends ContextJob {
private boolean isBlocked(GenericPdu pdu) {
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, Util.toIsoString(pdu.getFrom().getTextString()), false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString()))}, false);
return recipients.isBlocked();
}

View File

@ -21,12 +21,14 @@ import com.klinker.android.send_message.Utils;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.PartAuthority;
@ -45,8 +47,6 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.thoughtcrime.securesms.mms.MmsException;
public class MmsSendJob extends SendJob {
private static final long serialVersionUID = 0L;
@ -171,16 +171,16 @@ public class MmsSendJob extends SendJob {
{
SendReq req = new SendReq();
String lineNumber = Utils.getMyPhoneNumber(context);
List<String> numbers = message.getRecipients().toNumberStringList(true);
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
Address[] numbers = message.getRecipients().getAddresses();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber));
}
for (String recipient : numbers) {
req.addTo(new EncodedStringValue(recipient));
for (Address recipient : numbers) {
req.addTo(new EncodedStringValue(recipient.serialize()));
}
req.setDate(System.currentTimeMillis() / 1000);

View File

@ -10,16 +10,13 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.logging.Log;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.util.LinkedList;
@ -57,11 +54,7 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
while ((recipients = reader.getNext()) != null) {
if (recipients.isSingleRecipient()) {
try {
blocked.add(Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber()));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
blocked.add(recipients.getPrimaryRecipient().getAddress().toPhoneString());
}
}

View File

@ -6,13 +6,14 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
@ -20,9 +21,7 @@ import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalM
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.IdentityKey;
@ -50,19 +49,19 @@ import javax.inject.Inject;
public class MultiDeviceContactUpdateJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L;
private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName();
@Inject transient SignalMessageSenderFactory messageSenderFactory;
private final long recipientId;
private final @Nullable String address;
public MultiDeviceContactUpdateJob(Context context) {
this(context, -1);
this(context, null);
}
public MultiDeviceContactUpdateJob(Context context, long recipientId) {
public MultiDeviceContactUpdateJob(Context context, Address address) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withRequirement(new MasterSecretRequirement(context))
@ -70,7 +69,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
.withPersistence()
.create());
this.recipientId = recipientId;
this.address = address.serialize();
}
@Override
@ -82,11 +81,11 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
return;
}
if (recipientId <= 0) generateFullContactUpdate();
else generateSingleContactUpdate(recipientId);
if (address == null) generateFullContactUpdate();
else generateSingleContactUpdate(Address.fromSerialized(address));
}
private void generateSingleContactUpdate(long recipientId)
private void generateSingleContactUpdate(@NonNull Address address)
throws IOException, UntrustedIdentityException, NetworkException
{
SignalServiceMessageSender messageSender = messageSenderFactory.create();
@ -94,11 +93,11 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
Recipient recipient = RecipientFactory.getRecipientForId(context, recipientId, false);
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipientId);
Recipient recipient = RecipientFactory.getRecipientFor(context, address, false);
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
out.write(new DeviceContact(Util.canonicalizeNumber(context, recipient.getNumber()),
out.write(new DeviceContact(address.toPhoneString(),
Optional.fromNullable(recipient.getName()),
getAvatar(recipient.getContactUri()),
Optional.fromNullable(recipient.getColor().serialize()),
@ -126,14 +125,14 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
for (ContactData contactData : contacts) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id));
String number = Util.canonicalizeNumber(context, contactData.numbers.get(0).number);
Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, true).getPrimaryRecipient();
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getRecipientId());
Address address = Address.fromExternal(context, contactData.numbers.get(0).number);
Recipient recipient = RecipientFactory.getRecipientFor(context, address, false);
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
Optional<String> name = Optional.fromNullable(contactData.name);
Optional<String> color = getColor(number);
Optional<String> color = Optional.of(recipient.getColor().serialize());
out.write(new DeviceContact(number, name, getAvatar(contactUri), color, verified));
out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified));
}
out.close();
@ -161,15 +160,6 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
}
private Optional<String> getColor(String number) {
if (!TextUtils.isEmpty(number)) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false);
return Optional.of(recipients.getColor().serialize());
} else {
return Optional.absent();
}
}
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete)
throws IOException, UntrustedIdentityException, NetworkException
{
@ -245,7 +235,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
if (!identity.isPresent()) return Optional.absent();
String destination = Util.canonicalizeNumber(context, recipient.getNumber());
String destination = recipient.getAddress().toPhoneString();
IdentityKey identityKey = identity.get().getIdentityKey();
VerifiedMessage.VerifiedState state;

View File

@ -5,6 +5,7 @@ import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
@ -27,6 +28,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
@ -61,8 +64,14 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
reader = DatabaseFactory.getGroupDatabase(context).getGroups();
while ((record = reader.getNext()) != null) {
List<String> members = new LinkedList<>();
for (Address member : record.getMembers()) {
members.add(member.serialize());
}
out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()),
record.getMembers(), getAvatar(record.getAvatar()),
members, getAvatar(record.getAvatar()),
record.isActive()));
}

View File

@ -44,7 +44,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
this.messageIds = new LinkedList<>();
for (SyncMessageId messageId : messageIds) {
this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress(), messageId.getTimetamp()));
this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp()));
}
}

View File

@ -4,11 +4,11 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.IdentityKey;
@ -18,7 +18,6 @@ import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
@ -38,14 +37,14 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
private final VerifiedStatus verifiedStatus;
private final long timestamp;
public MultiDeviceVerifiedUpdateJob(Context context, String destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
public MultiDeviceVerifiedUpdateJob(Context context, Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
super(context, JobParameters.newBuilder()
.withRequirement(new NetworkRequirement(context))
.withPersistence()
.withGroupId("__MULTI_DEVICE_VERIFIED_UPDATE__")
.create());
this.destination = destination;
this.destination = destination.serialize();
this.identityKey = identityKey.serialize();
this.verifiedStatus = verifiedStatus;
this.timestamp = System.currentTimeMillis();
@ -64,13 +63,13 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
return;
}
String canonicalDestination = Util.canonicalizeNumber(context, destination);
Address canonicalDestination = Address.fromSerialized(destination);
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
SignalServiceMessageSender messageSender = messageSenderFactory.create();
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination, new IdentityKey(identityKey, 0), verifiedState, timestamp);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
} catch (InvalidNumberException | InvalidKeyException e) {
} catch (InvalidKeyException e) {
throw new IOException(e);
}
}

View File

@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -79,6 +80,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -92,11 +94,11 @@ public class PushDecryptJob extends ContextJob {
private final long messageId;
private final long smsMessageId;
public PushDecryptJob(Context context, long pushMessageId, String sender) {
this(context, pushMessageId, -1, sender);
public PushDecryptJob(Context context, long pushMessageId) {
this(context, pushMessageId, -1);
}
public PushDecryptJob(Context context, long pushMessageId, long smsMessageId, String sender) {
public PushDecryptJob(Context context, long pushMessageId, long smsMessageId) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withGroupId("__PUSH_DECRYPT_JOB__")
@ -221,7 +223,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, envelope.getSource());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, envelope.getTimestamp());
context.startService(intent);
@ -235,7 +237,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, envelope.getSource());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
context.startService(intent);
}
@ -248,7 +250,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, envelope.getSource());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
@ -267,7 +269,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, envelope.getSource());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
context.startService(intent);
}
}
@ -278,7 +280,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_NUMBER, envelope.getSource());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
context.startService(intent);
}
@ -288,7 +290,7 @@ public class PushDecryptJob extends ContextJob {
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(),
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
message.getTimestamp(),
"", Optional.<SignalServiceGroup>absent(), 0);
@ -328,7 +330,7 @@ public class PushDecryptJob extends ContextJob {
if (recipients.isSingleRecipient() && !recipients.isGroupRecipient()) {
SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(recipients.getPrimaryRecipient().getNumber());
sessionStore.deleteAllSessions(recipients.getPrimaryRecipient().getAddress().toPhoneString());
SecurityEvent.broadcastSecurityUpdateEvent(context);
@ -373,8 +375,10 @@ public class PushDecryptJob extends ContextJob {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
String localNumber = TextSecurePreferences.getLocalNumber(context);
Recipients recipients = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(),
localNumber, message.getTimestamp(), -1,
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()),
Address.fromSerialized(localNumber),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, true,
Optional.fromNullable(envelope.getRelay()),
Optional.<String>absent(), message.getGroupInfo(),
@ -458,8 +462,8 @@ public class PushDecryptJob extends ContextJob {
long envelopeTimestamp)
{
for (ReadMessage readMessage : readMessages) {
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(readMessage.getSender(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(readMessage.getSender(), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
for (Pair<Long, Long> expiringMessage : expiringText) {
ApplicationContext.getInstance(context)
@ -488,8 +492,10 @@ public class PushDecryptJob extends ContextJob {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
String localNumber = TextSecurePreferences.getLocalNumber(context);
Recipients recipients = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(),
localNumber, message.getTimestamp(), -1,
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()),
Address.fromSerialized(localNumber),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, false,
Optional.fromNullable(envelope.getRelay()),
message.getBody(),
@ -616,7 +622,7 @@ public class PushDecryptJob extends ContextJob {
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(masterSecret, smsMessageId.get(), body).second;
} else {
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(),
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
@ -764,14 +770,14 @@ public class PushDecryptJob extends ContextJob {
{
try {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
IdentityKey identityKey = whisperMessage.getIdentityKey();
String encoded = Base64.encodeBytes(serialized);
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress,
envelope.getSourceDevice(),
envelope.getTimestamp(), encoded,
Optional.<SignalServiceGroup>absent(), 0);
@ -780,13 +786,13 @@ public class PushDecryptJob extends ContextJob {
Optional<InsertResult> insertResult = database.insertMessageInbox(masterSecret, bundleMessage);
if (insertResult.isPresent()) {
database.setMismatchedIdentity(insertResult.get().getMessageId(), recipientId, identityKey);
database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey);
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
}
} else {
database.updateMessageBody(masterSecret, smsMessageId.get(), encoded);
database.markAsPreKeyBundle(smsMessageId.get());
database.setMismatchedIdentity(smsMessageId.get(), recipientId, identityKey);
database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey);
}
} catch (InvalidMessageException | InvalidVersionException e) {
throw new AssertionError(e);
@ -795,7 +801,8 @@ public class PushDecryptJob extends ContextJob {
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
envelope.getTimestamp(), "",
Optional.<SignalServiceGroup>absent(), 0);
@ -805,17 +812,17 @@ public class PushDecryptJob extends ContextJob {
private Recipients getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().getGroupInfo().isPresent()) {
return RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId()), false);
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId()))}, false);
} else {
return RecipientFactory.getRecipientsFromString(context, message.getDestination().get(), false);
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, message.getDestination().get())}, false);
}
}
private Recipients getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId()), false);
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId()))}, false);
} else {
return RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
return RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, envelope.getSource())}, false);
}
}
}

View File

@ -1,11 +1,14 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -17,7 +20,6 @@ import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
@ -51,20 +53,22 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Inject transient SignalMessageSenderFactory messageSenderFactory;
private final long messageId;
private final long filterRecipientId;
private final long messageId;
private final long filterRecipientId; // Deprecated
private final String filterAddress;
public PushGroupSendJob(Context context, long messageId, String destination, long filterRecipientId) {
public PushGroupSendJob(Context context, long messageId, @NonNull Address destination, @Nullable Address filterAddress) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withGroupId(destination)
.withGroupId(destination.toGroupString())
.withRequirement(new MasterSecretRequirement(context))
.withRequirement(new NetworkRequirement(context))
.withRetryCount(5)
.create());
this.messageId = messageId;
this.filterRecipientId = filterRecipientId;
this.filterAddress = filterAddress == null ? null :filterAddress.toPhoneString();
this.filterRecipientId = -1;
}
@Override
@ -79,7 +83,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
try {
deliver(masterSecret, message, filterRecipientId);
deliver(masterSecret, message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
@ -99,13 +103,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List<NetworkFailure> failures = new LinkedList<>();
for (NetworkFailureException nfe : e.getNetworkExceptions()) {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, nfe.getE164number(), false).getPrimaryRecipient();
failures.add(new NetworkFailure(recipient.getRecipientId()));
failures.add(new NetworkFailure(Address.fromSerialized(nfe.getE164number())));
}
for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false).getPrimaryRecipient();
database.addMismatchedIdentity(messageId, recipient.getRecipientId(), uie.getIdentityKey());
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
}
database.addFailures(messageId, failures);
@ -131,20 +133,21 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
}
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, long filterRecipientId)
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, @Nullable Address filterAddress)
throws IOException, RecipientFormattingException, InvalidNumberException,
EncapsulatedExceptions, UndeliverableMessageException
{
SignalServiceMessageSender messageSender = messageSenderFactory.create();
byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getNumber());
byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getAddress().toGroupString());
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
List<SignalServiceAddress> addresses;
if (filterRecipientId >= 0) addresses = getPushAddresses(filterRecipientId);
else addresses = getPushAddresses(recipients);
if (filterAddress != null) addresses = getPushAddresses(filterAddress);
else addresses = getPushAddresses(recipients);
if (message.isGroup()) {
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
@ -166,20 +169,19 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
}
}
private List<SignalServiceAddress> getPushAddresses(Recipients recipients) throws InvalidNumberException {
private List<SignalServiceAddress> getPushAddresses(Address address) {
List<SignalServiceAddress> addresses = new LinkedList<>();
addresses.add(getPushAddress(address));
return addresses;
}
private List<SignalServiceAddress> getPushAddresses(Recipients recipients) {
List<SignalServiceAddress> addresses = new LinkedList<>();
for (Recipient recipient : recipients.getRecipientsList()) {
addresses.add(getPushAddress(recipient.getNumber()));
addresses.add(getPushAddress(recipient.getAddress()));
}
return addresses;
}
private List<SignalServiceAddress> getPushAddresses(long filterRecipientId) throws InvalidNumberException {
List<SignalServiceAddress> addresses = new LinkedList<>();
addresses.add(getPushAddress(RecipientFactory.getRecipientForId(context, filterRecipientId, false).getNumber()));
return addresses;
}
}

View File

@ -4,6 +4,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@ -23,6 +24,8 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
@ -72,11 +75,16 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
.build();
}
List<String> members = new LinkedList<>();
for (Address member : record.getMembers()) {
members.add(member.serialize());
}
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
.withAvatar(avatar)
.withId(groupId)
.withMembers(record.getMembers())
.withMembers(members)
.withName(record.getTitle())
.build();

View File

@ -6,14 +6,14 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
@ -24,7 +24,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -32,8 +31,6 @@ import java.util.List;
import javax.inject.Inject;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
public class PushMediaSendJob extends PushSendJob implements InjectableType {
@ -46,7 +43,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
private final long messageId;
public PushMediaSendJob(Context context, long messageId, String destination) {
public PushMediaSendJob(Context context, long messageId, Address destination) {
super(context, constructParameters(context, destination));
this.messageId = messageId;
}
@ -82,10 +79,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context));
} catch (UntrustedIdentityException uie) {
Log.w(TAG, uie);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
database.addMismatchedIdentity(messageId, recipientId, uie.getIdentityKey());
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
database.markAsSentFailed(messageId);
}
}
@ -108,17 +102,14 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException
{
if (message.getRecipients() == null ||
message.getRecipients().getPrimaryRecipient() == null ||
message.getRecipients().getPrimaryRecipient().getNumber() == null)
{
if (message.getRecipients() == null || message.getRecipients().getPrimaryRecipient() == null) {
throw new UndeliverableMessageException("No destination address.");
}
SignalServiceMessageSender messageSender = messageSenderFactory.create();
try {
SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getNumber());
SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
@ -131,7 +122,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.build();
messageSender.sendMessage(address, mediaMessage);
} catch (InvalidNumberException | UnregisteredUserException e) {
} catch (UnregisteredUserException e) {
Log.w(TAG, e);
throw new InsecureFallbackApprovalException(e);
} catch (FileNotFoundException e) {

View File

@ -4,8 +4,8 @@ import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.NotInDirectoryException;
import org.thoughtcrime.securesms.database.TextSecureDirectory;
@ -26,33 +26,35 @@ public abstract class PushReceivedJob extends ContextJob {
}
public void handle(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) {
if (!isActiveNumber(context, envelope.getSource())) {
Address source = Address.fromExternal(context, envelope.getSource());
if (!isActiveNumber(context, source)) {
TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
ContactTokenDetails contactTokenDetails = new ContactTokenDetails();
contactTokenDetails.setNumber(envelope.getSource());
directory.setNumber(contactTokenDetails, true);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipients));
}
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
handleMessage(envelope, sendExplicitReceipt);
handleMessage(envelope, source, sendExplicitReceipt);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
}
}
private void handleMessage(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
private void handleMessage(SignalServiceEnvelope envelope, Address source, boolean sendExplicitReceipt) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {source}, false);
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
if (!recipients.isBlocked()) {
long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope);
jobManager.add(new PushDecryptJob(context, messageId, envelope.getSource()));
jobManager.add(new PushDecryptJob(context, messageId));
} else {
Log.w(TAG, "*** Received blocked push message, ignoring...");
}
@ -66,15 +68,15 @@ public abstract class PushReceivedJob extends ContextJob {
private void handleReceipt(SignalServiceEnvelope envelope) {
Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(envelope.getSource(),
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()),
envelope.getTimestamp()));
}
private boolean isActiveNumber(Context context, String e164number) {
private boolean isActiveNumber(Context context, Address address) {
boolean isActiveNumber;
try {
isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(e164number);
isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(address);
} catch (NotInDirectoryException e) {
isActiveNumber = false;
}

View File

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.TextSecureExpiredException;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.TextSecureDirectory;
import org.thoughtcrime.securesms.events.PartProgressEvent;
@ -16,14 +17,12 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.io.InputStream;
@ -38,10 +37,10 @@ public abstract class PushSendJob extends SendJob {
super(context, parameters);
}
protected static JobParameters constructParameters(Context context, String destination) {
protected static JobParameters constructParameters(Context context, Address destination) {
JobParameters.Builder builder = JobParameters.newBuilder();
builder.withPersistence();
builder.withGroupId(destination);
builder.withGroupId(destination.serialize());
builder.withRequirement(new MasterSecretRequirement(context));
builder.withRequirement(new NetworkRequirement(context));
builder.withRetryCount(5);
@ -62,10 +61,9 @@ public abstract class PushSendJob extends SendJob {
onPushSend(masterSecret);
}
protected SignalServiceAddress getPushAddress(String number) throws InvalidNumberException {
String e164number = Util.canonicalizeNumber(context, number);
String relay = TextSecureDirectory.getInstance(context).getRelay(e164number);
return new SignalServiceAddress(e164number, Optional.fromNullable(relay));
protected SignalServiceAddress getPushAddress(Address address) {
String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString());
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
}
protected List<SignalServiceAttachment> getAttachmentsFor(MasterSecret masterSecret, List<Attachment> parts) {

View File

@ -5,14 +5,13 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
@ -22,7 +21,6 @@ import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
@ -40,7 +38,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
private final long messageId;
public PushTextSendJob(Context context, long messageId, String destination) {
public PushTextSendJob(Context context, long messageId, Address destination) {
super(context, constructParameters(context, destination));
this.messageId = messageId;
}
@ -72,10 +70,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context));
} catch (UntrustedIdentityException e) {
Log.w(TAG, e);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, e.getE164Number(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
database.addMismatchedIdentity(record.getId(), recipientId, e.getIdentityKey());
database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey());
database.markAsSentFailed(record.getId());
database.markAsPush(record.getId());
}
@ -104,7 +99,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException
{
try {
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getNumber());
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
SignalServiceMessageSender messageSender = messageSenderFactory.create();
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
@ -115,7 +110,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
messageSender.sendMessage(address, textSecureMessage);
} catch (InvalidNumberException | UnregisteredUserException e) {
} catch (UnregisteredUserException e) {
Log.w(TAG, e);
throw new InsecureFallbackApprovalException(e);
} catch (IOException e) {

View File

@ -9,13 +9,11 @@ import android.util.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.MessageRetrievalService;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@ -35,14 +33,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
@Inject transient SignalServiceMessageReceiver receiver;
private final long[] recipientIds;
private final Recipients recipients;
public RetrieveProfileJob(Context context, Recipients recipients) {
super(context, JobParameters.newBuilder()
.withRetryCount(3)
.create());
this.recipientIds = recipients.getIds();
this.recipients = recipients;
}
@Override
@ -51,8 +49,6 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
@Override
public void onRun() throws IOException, InvalidKeyException {
try {
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, true);
for (Recipient recipient : recipients) {
if (recipient.isGroupRecipient()) handleGroupRecipient(recipient);
else handleIndividualRecipient(recipient);
@ -73,7 +69,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
private void handleIndividualRecipient(Recipient recipient)
throws IOException, InvalidKeyException, InvalidNumberException
{
String number = Util.canonicalizeNumber(context, recipient.getNumber());
String number = recipient.getAddress().toPhoneString();
SignalServiceProfile profile = retrieveProfile(number);
if (TextUtils.isEmpty(profile.getIdentityKey())) {
@ -84,7 +80,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
IdentityKey identityKey = new IdentityKey(Base64.decode(profile.getIdentityKey()), 0);
if (!DatabaseFactory.getIdentityDatabase(context)
.getIdentity(recipient.getRecipientId())
.getIdentity(recipient.getAddress())
.isPresent())
{
Log.w(TAG, "Still first use...");
@ -97,7 +93,7 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
private void handleGroupRecipient(Recipient group)
throws IOException, InvalidKeyException, InvalidNumberException
{
byte[] groupId = GroupUtil.getDecodedId(group.getNumber());
byte[] groupId = GroupUtil.getDecodedId(group.getAddress().toGroupString());
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
for (Recipient recipient : recipients) {

View File

@ -9,6 +9,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
@ -85,7 +86,7 @@ public class SmsReceiveJob extends ContextJob {
private boolean isBlocked(IncomingTextMessage message) {
if (message.getSender() != null) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, false);
return recipients.isBlocked();
}
@ -114,7 +115,7 @@ public class SmsReceiveJob extends ContextJob {
List<IncomingTextMessage> messages = new LinkedList<>();
for (Object pdu : pdus) {
messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[])pdu), subscriptionId));
messages.add(new IncomingTextMessage(context, SmsMessage.createFromPdu((byte[])pdu), subscriptionId));
}
if (messages.isEmpty()) {

View File

@ -80,7 +80,7 @@ public class SmsSendJob extends SendJob {
throw new UndeliverableMessageException("Trying to send a secure SMS?");
}
String recipient = message.getIndividualRecipient().getNumber();
String recipient = message.getIndividualRecipient().getAddress().serialize();
// See issue #1516 for bug report, and discussion on commits related to #4833 for problems
// related to the original fix to #1516. This still may not be a correct fix if networks allow

View File

@ -7,8 +7,6 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -17,7 +15,6 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.state.SessionStore;
public class SmsSentJob extends MasterSecretJob {
@ -85,7 +82,7 @@ public class SmsSentJob extends MasterSecretJob {
Log.w(TAG, "Service connectivity problem, requeuing...");
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getNumber()));
.add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getAddress().serialize()));
break;
default:
database.markAsSentFailed(messageId);

View File

@ -1,8 +1,11 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MmsAddresses;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
@ -14,25 +17,25 @@ import java.util.List;
public class IncomingMediaMessage {
private final String from;
private final Address from;
private final String body;
private final String groupId;
private final Address groupId;
private final boolean push;
private final long sentTimeMillis;
private final int subscriptionId;
private final long expiresIn;
private final boolean expirationUpdate;
private final List<String> to = new LinkedList<>();
private final List<String> cc = new LinkedList<>();
private final List<Address> to = new LinkedList<>();
private final List<Address> cc = new LinkedList<>();
private final List<Attachment> attachments = new LinkedList<>();
public IncomingMediaMessage(String from, List<String> to, List<String> cc,
public IncomingMediaMessage(Context context, String from, List<String> to, List<String> cc,
String body, long sentTimeMillis,
List<Attachment> attachments, int subscriptionId,
long expiresIn, boolean expirationUpdate)
{
this.from = from;
this.from = Address.fromExternal(context, from);
this.sentTimeMillis = sentTimeMillis;
this.body = body;
this.groupId = null;
@ -41,14 +44,20 @@ public class IncomingMediaMessage {
this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate;
this.to.addAll(to);
this.cc.addAll(cc);
for (String destination : to) {
this.to.add(Address.fromExternal(context, destination));
}
for (String destination : cc) {
this.cc.add(Address.fromExternal(context, destination));
}
this.attachments.addAll(attachments);
}
public IncomingMediaMessage(MasterSecretUnion masterSecret,
String from,
String to,
Address from,
Address to,
long sentTimeMillis,
int subscriptionId,
long expiresIn,
@ -66,7 +75,7 @@ public class IncomingMediaMessage {
this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate;
if (group.isPresent()) this.groupId = GroupUtil.getEncodedId(group.get().getGroupId());
if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId()));
else this.groupId = null;
this.to.add(to);
@ -82,14 +91,14 @@ public class IncomingMediaMessage {
}
public MmsAddresses getAddresses() {
return new MmsAddresses(from, to, cc, new LinkedList<String>());
return new MmsAddresses(from, to, cc, new LinkedList<Address>());
}
public List<Attachment> getAttachments() {
return attachments;
}
public String getGroupId() {
public Address getGroupId() {
return groupId;
}

View File

@ -17,7 +17,6 @@
package org.thoughtcrime.securesms.notifications;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
@ -27,10 +26,9 @@ import android.support.v4.app.RemoteInput;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -48,11 +46,11 @@ import java.util.List;
*/
public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName();
public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY";
public static final String RECIPIENT_IDS_EXTRA = "car_recipient_ids";
public static final String VOICE_REPLY_KEY = "car_voice_reply_key";
public static final String THREAD_ID_EXTRA = "car_reply_thread_id";
public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName();
public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY";
public static final String ADDRESSES_EXTRA = "car_addresses";
public static final String VOICE_REPLY_KEY = "car_voice_reply_key";
public static final String THREAD_ID_EXTRA = "car_reply_thread_id";
@Override
protected void onReceive(final Context context, Intent intent,
@ -64,10 +62,10 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
if (remoteInput == null) return;
final long[] recipientIds = intent.getLongArrayExtra(RECIPIENT_IDS_EXTRA);
final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA));
final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1);
final CharSequence responseText = getMessageText(intent);
final Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
final Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false);
if (responseText != null) {
new AsyncTask<Void, Void, Void>() {
@ -76,7 +74,7 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
long replyThreadId;
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses);
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;

View File

@ -107,7 +107,7 @@ public class MessageNotifier {
sendInThreadNotification(context, recipients);
} else {
Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.setData((Uri.parse("custom://" + System.currentTimeMillis())));

View File

@ -70,7 +70,7 @@ public class NotificationItem {
public PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, ConversationActivity.class);
Recipients notifyRecipients = threadRecipients != null ? threadRecipients : recipients;
if (notifyRecipients != null) intent.putExtra("recipients", notifyRecipients.getIds());
if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, notifyRecipients.getAddresses());
intent.putExtra("thread_id", threadId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));

View File

@ -121,7 +121,7 @@ public class NotificationState {
Intent intent = new Intent(RemoteReplyReceiver.REPLY_ACTION);
intent.setClass(context, RemoteReplyReceiver.class);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
intent.putExtra(RemoteReplyReceiver.RECIPIENT_IDS_EXTRA, recipients.getIds());
intent.putExtra(RemoteReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses());
intent.setPackage(context.getPackageName());
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@ -134,7 +134,7 @@ public class NotificationState {
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.setClass(context, AndroidAutoReplyReceiver.class);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
intent.putExtra(AndroidAutoReplyReceiver.RECIPIENT_IDS_EXTRA, recipients.getIds());
intent.putExtra(AndroidAutoReplyReceiver.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(AndroidAutoReplyReceiver.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
intent.setPackage(context.getPackageName());
@ -164,7 +164,7 @@ public class NotificationState {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications! " + threads.size());
Intent intent = new Intent(context, ConversationPopupActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
intent.putExtra(ConversationActivity.ADDRESSES_EXTRA, recipients.getAddresses());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));

View File

@ -26,6 +26,7 @@ import android.support.v4.app.RemoteInput;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
@ -44,9 +45,9 @@ import java.util.List;
*/
public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
public static final String TAG = RemoteReplyReceiver.class.getSimpleName();
public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY";
public static final String RECIPIENT_IDS_EXTRA = "recipient_ids";
public static final String TAG = RemoteReplyReceiver.class.getSimpleName();
public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY";
public static final String ADDRESSES_EXTRA = "addresses";
@Override
protected void onReceive(final Context context, Intent intent,
@ -58,7 +59,7 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
if (remoteInput == null) return;
final long[] recipientIds = intent.getLongArrayExtra(RECIPIENT_IDS_EXTRA);
final Address[] addresses = Address.fromParcelable(intent.getParcelableArrayExtra(ADDRESSES_EXTRA));
final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_REMOTE_REPLY);
if (masterSecret != null && responseText != null) {
@ -67,11 +68,11 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
protected Void doInBackground(Void... params) {
long threadId;
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(addresses);
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses, false);
if (recipients.isGroupRecipient()) {
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
threadId = MessageSender.send(context, masterSecret, reply, -1, false, null);

View File

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.notifications;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@ -19,6 +18,8 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.Slide;
@ -69,11 +70,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
.toConversationColor(context)));
} else {
setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal));
setLargeIcon(Recipient.getUnknownRecipient()
.getContactPhoto()
.asDrawable(context, Recipient.getUnknownRecipient()
.getColor()
.toConversationColor(context)));
setLargeIcon(ContactPhotoFactory.getDefaultContactPhoto("Unknown")
.asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context)));
}
}

View File

@ -25,9 +25,9 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import java.util.Collections;
@ -42,9 +42,8 @@ public class Recipient {
private final Set<RecipientModifiedListener> listeners = Collections.newSetFromMap(new WeakHashMap<RecipientModifiedListener, Boolean>());
private final long recipientId;
private final @NonNull Address address;
private @NonNull String number;
private @Nullable String name;
private @Nullable String customLabel;
private boolean stale;
@ -55,13 +54,11 @@ public class Recipient {
@Nullable private MaterialColor color;
Recipient(long recipientId,
@NonNull String number,
Recipient(@NonNull Address address,
@Nullable Recipient stale,
@NonNull ListenableFutureTask<RecipientDetails> future)
{
this.recipientId = recipientId;
this.number = number;
this.address = address;
this.contactPhoto = ContactPhotoFactory.getLoadingPhoto();
this.color = null;
this.resolving = true;
@ -80,7 +77,6 @@ public class Recipient {
if (result != null) {
synchronized (Recipient.this) {
Recipient.this.name = result.name;
Recipient.this.number = result.number;
Recipient.this.contactUri = result.contactUri;
Recipient.this.contactPhoto = result.avatar;
Recipient.this.color = result.color;
@ -99,9 +95,8 @@ public class Recipient {
});
}
Recipient(long recipientId, RecipientDetails details) {
this.recipientId = recipientId;
this.number = details.number;
Recipient(Address address, RecipientDetails details) {
this.address = address;
this.contactUri = details.contactUri;
this.name = details.name;
this.contactPhoto = details.avatar;
@ -132,20 +127,16 @@ public class Recipient {
notifyListeners();
}
public @NonNull String getNumber() {
return number;
public @NonNull Address getAddress() {
return address;
}
public @Nullable String getCustomLabel() {
return customLabel;
}
public long getRecipientId() {
return recipientId;
}
public boolean isGroupRecipient() {
return GroupUtil.isEncodedGroup(number);
return address.isGroup();
}
public synchronized void addListener(RecipientModifiedListener listener) {
@ -157,17 +148,13 @@ public class Recipient {
}
public synchronized String toShortString() {
return (name == null ? number : name);
return (name == null ? address.serialize() : name);
}
public synchronized @NonNull ContactPhoto getContactPhoto() {
return contactPhoto;
}
public static Recipient getUnknownRecipient() {
return new Recipient(-1, new RecipientDetails("Unknown", "Unknown", null, null,
ContactPhotoFactory.getDefaultContactPhoto(null), null));
}
@Override
public boolean equals(Object o) {
@ -176,12 +163,12 @@ public class Recipient {
Recipient that = (Recipient) o;
return this.recipientId == that.recipientId;
return this.address.equals(that.address);
}
@Override
public int hashCode() {
return 31 + (int)this.recipientId;
return this.address.hashCode();
}
private void notifyListeners() {

View File

@ -19,16 +19,10 @@ package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.thoughtcrime.securesms.database.Address;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
public class RecipientFactory {
@ -36,104 +30,32 @@ public class RecipientFactory {
private static final RecipientProvider provider = new RecipientProvider();
public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) {
if (TextUtils.isEmpty(recipientIds))
return new Recipients();
return getRecipientsForIds(context, Util.split(recipientIds, " "), asynchronous);
}
public static @NonNull Recipients getRecipientsFor(Context context, Collection<Recipient> recipients, boolean asynchronous) {
long[] ids = new long[recipients.size()];
Address[] addresses= new Address[recipients.size()];
int i = 0;
for (Recipient recipient : recipients) {
ids[i++] = recipient.getRecipientId();
addresses[i++] = recipient.getAddress();
}
return provider.getRecipients(context, ids, asynchronous);
return provider.getRecipients(context, addresses, asynchronous);
}
public static Recipients getRecipientsFor(Context context, Recipient recipient, boolean asynchronous) {
long[] ids = new long[1];
ids[0] = recipient.getRecipientId();
Address[] addresses = new Address[1];
addresses[0] = recipient.getAddress();
return provider.getRecipients(context, ids, asynchronous);
return provider.getRecipients(context, addresses, asynchronous);
}
public @NonNull static Recipient getRecipientForId(Context context, long recipientId, boolean asynchronous) {
return provider.getRecipient(context, recipientId, asynchronous);
public static @NonNull Recipients getRecipientsFor(@NonNull Context context, @NonNull Address[] addresses, boolean asynchronous) {
if (addresses == null || addresses.length == 0) throw new AssertionError(addresses);
return provider.getRecipients(context, addresses, asynchronous);
}
public @NonNull static Recipients getRecipientsForIds(Context context, long[] recipientIds, boolean asynchronous) {
return provider.getRecipients(context, recipientIds, asynchronous);
}
public static @NonNull Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
List<String> ids = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
Optional<Long> id = getRecipientIdFromNumber(context, tokenizer.nextToken());
if (id.isPresent()) {
ids.add(String.valueOf(id.get()));
}
}
return getRecipientsForIds(context, ids, asynchronous);
}
public static @NonNull Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List<String> numbers, boolean asynchronous) {
List<String> ids = new LinkedList<>();
for (String number : numbers) {
Optional<Long> id = getRecipientIdFromNumber(context, number);
if (id.isPresent()) {
ids.add(String.valueOf(id.get()));
}
}
return getRecipientsForIds(context, ids, asynchronous);
}
private static @NonNull Recipients getRecipientsForIds(Context context, List<String> idStrings, boolean asynchronous) {
long[] ids = new long[idStrings.size()];
int i = 0;
for (String id : idStrings) {
ids[i++] = Long.parseLong(id);
}
return provider.getRecipients(context, ids, asynchronous);
}
private static Optional<Long> getRecipientIdFromNumber(Context context, String number) {
number = number.trim();
if (number.isEmpty()) return Optional.absent();
if (hasBracketedNumber(number)) {
number = parseBracketedNumber(number);
}
return Optional.of(CanonicalAddressDatabase.getInstance(context).getCanonicalAddressId(number));
}
private static boolean hasBracketedNumber(String recipient) {
int openBracketIndex = recipient.indexOf('<');
return (openBracketIndex != -1) &&
(recipient.indexOf('>', openBracketIndex) != -1);
}
private static String parseBracketedNumber(String recipient) {
int begin = recipient.indexOf('<');
int end = recipient.indexOf('>', begin);
String value = recipient.substring(begin + 1, end);
return value;
public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
if (address == null) throw new AssertionError(address);
return provider.getRecipient(context, address, asynchronous);
}
public static void clearCache(Context context) {

View File

@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
@ -66,45 +66,43 @@ class RecipientProvider {
};
private static final Map<String, RecipientDetails> STATIC_DETAILS = new HashMap<String, RecipientDetails>() {{
put("262966", new RecipientDetails("Amazon", "262966", null, null,
put("262966", new RecipientDetails("Amazon", null, null,
ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_amazon),
ContactColors.UNKNOWN_COLOR));
}};
@NonNull Recipient getRecipient(Context context, long recipientId, boolean asynchronous) {
Recipient cachedRecipient = recipientCache.get(recipientId);
@NonNull Recipient getRecipient(Context context, Address address, boolean asynchronous) {
Recipient cachedRecipient = recipientCache.get(address);
if (cachedRecipient != null && !cachedRecipient.isStale() && (asynchronous || !cachedRecipient.isResolving())) {
return cachedRecipient;
}
String number = CanonicalAddressDatabase.getInstance(context).getAddressFromId(recipientId);
if (asynchronous) {
cachedRecipient = new Recipient(recipientId, number, cachedRecipient, getRecipientDetailsAsync(context, recipientId, number));
cachedRecipient = new Recipient(address, cachedRecipient, getRecipientDetailsAsync(context, address));
} else {
cachedRecipient = new Recipient(recipientId, getRecipientDetailsSync(context, recipientId, number));
cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address));
}
recipientCache.set(recipientId, cachedRecipient);
recipientCache.set(address, cachedRecipient);
return cachedRecipient;
}
@NonNull Recipients getRecipients(Context context, long[] recipientIds, boolean asynchronous) {
Recipients cachedRecipients = recipientsCache.get(new RecipientIds(recipientIds));
@NonNull Recipients getRecipients(Context context, Address[] recipientAddresses, boolean asynchronous) {
Recipients cachedRecipients = recipientsCache.get(new RecipientAddresses(recipientAddresses));
if (cachedRecipients != null && !cachedRecipients.isStale() && (asynchronous || !cachedRecipients.isResolving())) {
return cachedRecipients;
}
List<Recipient> recipientList = new LinkedList<>();
for (long recipientId : recipientIds) {
recipientList.add(getRecipient(context, recipientId, asynchronous));
for (Address address : recipientAddresses) {
recipientList.add(getRecipient(context, address, asynchronous));
}
if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientIds));
else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientIds));
if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientAddresses));
else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientAddresses));
recipientsCache.set(new RecipientIds(recipientIds), cachedRecipients);
recipientsCache.set(new RecipientAddresses(recipientAddresses), cachedRecipients);
return cachedRecipients;
}
@ -113,14 +111,12 @@ class RecipientProvider {
recipientsCache.reset();
}
private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context,
final long recipientId,
final @NonNull String number)
private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context, final @NonNull Address address)
{
Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
@Override
public RecipientDetails call() throws Exception {
return getRecipientDetailsSync(context, recipientId, number);
return getRecipientDetailsSync(context, address);
}
};
@ -129,15 +125,15 @@ class RecipientProvider {
return future;
}
private @NonNull RecipientDetails getRecipientDetailsSync(Context context, long recipientId, @NonNull String number) {
if (GroupUtil.isEncodedGroup(number)) return getGroupRecipientDetails(context, number);
else return getIndividualRecipientDetails(context, recipientId, number);
private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address) {
if (address.isGroup()) return getGroupRecipientDetails(context, address);
else return getIndividualRecipientDetails(context, address);
}
private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, long recipientId, @NonNull String number) {
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new long[]{recipientId});
private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address) {
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new Address[]{address});
MaterialColor color = preferences.isPresent() ? preferences.get().getColor() : null;
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION,
null, null, null);
@ -151,7 +147,7 @@ class RecipientProvider {
Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""),
name);
return new RecipientDetails(cursor.getString(0), resultNumber, cursor.getString(4), contactUri, contactPhoto, color);
return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, color);
} else {
Log.w(TAG, "resultNumber is null");
}
@ -161,14 +157,14 @@ class RecipientProvider {
cursor.close();
}
if (STATIC_DETAILS.containsKey(number)) return STATIC_DETAILS.get(number);
else return new RecipientDetails(null, number, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), color);
if (STATIC_DETAILS.containsKey(address.toPhoneString())) return STATIC_DETAILS.get(address.toPhoneString());
else return new RecipientDetails(null, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), color);
}
private @NonNull RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId) {
try {
GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context)
.getGroup(GroupUtil.getDecodedId(groupId));
.getGroup(GroupUtil.getDecodedId(groupId.toGroupString()));
if (record != null) {
ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar());
@ -178,27 +174,27 @@ class RecipientProvider {
title = context.getString(R.string.RecipientProvider_unnamed_group);;
}
return new RecipientDetails(title, groupId, null, null, contactPhoto, null);
return new RecipientDetails(title, null, null, contactPhoto, null);
}
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), groupId, null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null);
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null);
} catch (IOException e) {
Log.w("RecipientProvider", e);
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), groupId, null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null);
return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null);
}
}
private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, long[] recipientIds) {
private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, Address[] addresses) {
return DatabaseFactory.getRecipientPreferenceDatabase(context)
.getRecipientsPreferences(recipientIds)
.getRecipientsPreferences(addresses)
.orNull();
}
private ListenableFutureTask<RecipientsPreferences> getRecipientsPreferencesAsync(final Context context, final long[] recipientIds) {
private ListenableFutureTask<RecipientsPreferences> getRecipientsPreferencesAsync(final Context context, final Address[] addresses) {
ListenableFutureTask<RecipientsPreferences> task = new ListenableFutureTask<>(new Callable<RecipientsPreferences>() {
@Override
public RecipientsPreferences call() throws Exception {
return getRecipientsPreferencesSync(context, recipientIds);
return getRecipientsPreferencesSync(context, addresses);
}
});
@ -209,52 +205,50 @@ class RecipientProvider {
public static class RecipientDetails {
@Nullable public final String name;
@NonNull public final String number;
@Nullable public final String customLabel;
@NonNull public final ContactPhoto avatar;
@Nullable public final Uri contactUri;
@Nullable public final MaterialColor color;
public RecipientDetails(@Nullable String name, @NonNull String number,
@Nullable String customLabel, @Nullable Uri contactUri,
@NonNull ContactPhoto avatar, @Nullable MaterialColor color)
public RecipientDetails(@Nullable String name, @Nullable String customLabel,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar,
@Nullable MaterialColor color)
{
this.name = name;
this.customLabel = customLabel;
this.number = number;
this.avatar = avatar;
this.contactUri = contactUri;
this.color = color;
}
}
private static class RecipientIds {
private final long[] ids;
private static class RecipientAddresses {
private final Address[] addresses;
private RecipientIds(long[] ids) {
this.ids = ids;
private RecipientAddresses(Address[] addresses) {
this.addresses = addresses;
}
public boolean equals(Object other) {
if (other == null || !(other instanceof RecipientIds)) return false;
return Arrays.equals(this.ids, ((RecipientIds) other).ids);
if (other == null || !(other instanceof RecipientAddresses)) return false;
return Arrays.equals(this.addresses, ((RecipientAddresses) other).addresses);
}
public int hashCode() {
return Arrays.hashCode(ids);
return Arrays.hashCode(addresses);
}
}
private static class RecipientCache {
private final Map<Long,Recipient> cache = new LRUCache<>(1000);
private final Map<Address,Recipient> cache = new LRUCache<>(1000);
public synchronized Recipient get(long recipientId) {
return cache.get(recipientId);
public synchronized Recipient get(Address address) {
return cache.get(address);
}
public synchronized void set(long recipientId, Recipient recipient) {
cache.put(recipientId, recipient);
public synchronized void set(Address address, Recipient recipient) {
cache.put(address, recipient);
}
public synchronized void reset() {
@ -267,14 +261,14 @@ class RecipientProvider {
private static class RecipientsCache {
private final Map<RecipientIds,Recipients> cache = new LRUCache<>(1000);
private final Map<RecipientAddresses,Recipients> cache = new LRUCache<>(1000);
public synchronized Recipients get(RecipientIds ids) {
return cache.get(ids);
public synchronized Recipients get(RecipientAddresses addresses) {
return cache.get(addresses);
}
public synchronized void set(RecipientIds ids, Recipients recipients) {
cache.put(ids, recipients);
public synchronized void set(RecipientAddresses addresses, Recipients recipients) {
cache.put(addresses, recipients);
}
public synchronized void reset() {

View File

@ -20,20 +20,17 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Patterns;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.Arrays;
import java.util.Collections;
@ -217,15 +214,16 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
public boolean isEmailRecipient() {
for (Recipient recipient : recipients) {
if (NumberUtil.isValidEmail(recipient.getNumber()))
if (recipient.getAddress().isEmail()) {
return true;
}
}
return false;
}
public boolean isGroupRecipient() {
return isSingleRecipient() && GroupUtil.isEncodedGroup(recipients.get(0).getNumber());
return isSingleRecipient() && recipients.get(0).getAddress().isGroup();
}
public boolean isEmpty() {
@ -247,57 +245,20 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
return this.recipients;
}
public long[] getIds() {
long[] ids = new long[recipients.size()];
for (int i=0; i<recipients.size(); i++) {
ids[i] = recipients.get(i).getRecipientId();
public Address[] getAddresses() {
Address[] addresses = new Address[recipients.size()];
for (int i=0;i<recipients.size();i++) {
addresses[i] = recipients.get(i).getAddress();
}
return ids;
Arrays.sort(addresses);
return addresses;
}
public String getSortedIdsString() {
Set<Long> recipientSet = new HashSet<>();
for (Recipient recipient : this.recipients) {
recipientSet.add(recipient.getRecipientId());
}
long[] recipientArray = new long[recipientSet.size()];
int i = 0;
for (Long recipientId : recipientSet) {
recipientArray[i++] = recipientId;
}
Arrays.sort(recipientArray);
return Util.join(recipientArray, " ");
}
public @NonNull String[] toNumberStringArray(boolean scrub) {
String[] recipientsArray = new String[recipients.size()];
Iterator<Recipient> iterator = recipients.iterator();
int i = 0;
while (iterator.hasNext()) {
String number = iterator.next().getNumber();
if (scrub && number != null &&
!Patterns.EMAIL_ADDRESS.matcher(number).matches() &&
!GroupUtil.isEncodedGroup(number))
{
number = number.replaceAll("[^0-9+]", "");
}
recipientsArray[i++] = number;
}
return recipientsArray;
}
public @NonNull List<String> toNumberStringList(boolean scrub) {
List<String> results = new LinkedList<>();
Collections.addAll(results, toNumberStringArray(scrub));
public List<Address> getAddressesList() {
List<Address> results = new LinkedList<>();
Collections.addAll(results, getAddresses());
return results;
}

View File

@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import android.support.annotation.RequiresApi;
@ -48,17 +49,22 @@ public class DirectShareService extends ChooserTargetService {
ThreadRecord record;
while ((record = reader.getNext()) != null && results.size() < 10) {
Recipients recipients = RecipientFactory.getRecipientsForIds(this, record.getRecipients().getIds(), false);
Recipients recipients = RecipientFactory.getRecipientsFor(this, record.getRecipients().getAddresses(), false);
String name = recipients.toShortString();
Drawable drawable = recipients.getContactPhoto().asDrawable(this, recipients.getColor().toConversationColor(this));
Bitmap avatar = BitmapUtil.createFromDrawable(drawable, 500, 500);
Parcel parcel = Parcel.obtain();
parcel.writeTypedArray(recipients.getAddresses(), 0);
Bundle bundle = new Bundle();
bundle.putLong(ShareActivity.EXTRA_THREAD_ID, record.getThreadId());
bundle.putLongArray(ShareActivity.EXTRA_RECIPIENT_IDS, recipients.getIds());
bundle.putByteArray(ShareActivity.EXTRA_ADDRESSES_MARSHALLED, parcel.marshall());
bundle.putInt(ShareActivity.EXTRA_DISTRIBUTION_TYPE, record.getDistributionType());
bundle.setClassLoader(getClassLoader());
results.add(new ChooserTarget(name, Icon.createWithBitmap(avatar), 1.0f, componentName, bundle));
parcel.recycle();
}
return results;

View File

@ -9,6 +9,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.database.ThreadDatabase;
@ -49,12 +50,20 @@ public class QuickResponseService extends MasterSecretIntentService {
Rfc5724Uri uri = new Rfc5724Uri(intent.getDataString());
String content = intent.getStringExtra(Intent.EXTRA_TEXT);
String numbers = uri.getPath();
if(numbers.contains("%")){
if (numbers.contains("%")){
numbers = URLDecoder.decode(numbers);
}
Recipients recipients = RecipientFactory.getRecipientsFromString(this, numbers, false);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getIds());
String[] numbersArray = numbers.split(",");
Address[] addresses = new Address[numbersArray.length];
for (int i=0;i<numbersArray.length;i++) {
addresses[i] = Address.fromExternal(this, numbersArray[i]);
}
Recipients recipients = RecipientFactory.getRecipientsFor(this, addresses, false);
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getAddresses());
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;

View File

@ -16,12 +16,11 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -237,7 +236,7 @@ public class RegistrationService extends Service {
throws IOException
{
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient();
Address self = Address.fromSerialized(number);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this);
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(this);
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, identityKey, true);
@ -257,7 +256,7 @@ public class RegistrationService extends Service {
TextSecurePreferences.setWebsocketRegistered(this, true);
DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true);
DatabaseFactory.getIdentityDatabase(this).saveIdentity(self, identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, true, System.currentTimeMillis(), true);
DirectoryHelper.refreshDirectory(this, accountManager, number);
DirectoryRefreshListener.schedule(this);

Some files were not shown because too many files have changed in this diff Show More