2015-01-15 21:35:35 +00:00
|
|
|
package org.thoughtcrime.securesms;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.DialogInterface;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.os.AsyncTask;
|
2015-05-21 17:13:03 +00:00
|
|
|
import android.support.v7.app.AlertDialog;
|
2015-01-15 21:35:35 +00:00
|
|
|
import android.text.SpannableString;
|
|
|
|
import android.text.Spanned;
|
|
|
|
import android.text.method.LinkMovementMethod;
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
2017-05-20 01:01:40 +00:00
|
|
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
2017-07-26 16:59:15 +00:00
|
|
|
import org.thoughtcrime.securesms.database.Address;
|
2015-01-15 21:35:35 +00:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.PushDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
|
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
|
|
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
|
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
|
|
|
import org.thoughtcrime.securesms.util.Base64;
|
2016-11-09 17:37:40 +00:00
|
|
|
import org.thoughtcrime.securesms.util.VerifySpan;
|
2017-05-20 01:01:40 +00:00
|
|
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
2016-03-23 17:34:41 +00:00
|
|
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
|
|
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
2015-01-15 21:35:35 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
2017-05-20 01:01:40 +00:00
|
|
|
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
public class ConfirmIdentityDialog extends AlertDialog {
|
|
|
|
|
|
|
|
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
|
|
|
|
|
|
|
|
private OnClickListener callback;
|
|
|
|
|
|
|
|
public ConfirmIdentityDialog(Context context,
|
|
|
|
MasterSecret masterSecret,
|
|
|
|
MessageRecord messageRecord,
|
|
|
|
IdentityKeyMismatch mismatch)
|
|
|
|
{
|
|
|
|
super(context);
|
|
|
|
|
2017-08-22 01:32:38 +00:00
|
|
|
Recipient recipient = Recipient.from(context, mismatch.getAddress(), false);
|
2017-02-19 20:29:08 +00:00
|
|
|
String name = recipient.toShortString();
|
|
|
|
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));
|
|
|
|
|
|
|
|
spannableString.setSpan(new VerifySpan(context, mismatch),
|
|
|
|
introduction.length()+1, spannableString.length(),
|
|
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
|
|
|
|
setTitle(name);
|
|
|
|
setMessage(spannableString);
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, recipient.getAddress()));
|
2017-02-19 20:29:08 +00:00
|
|
|
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
|
2015-01-15 21:35:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void show() {
|
|
|
|
super.show();
|
|
|
|
((TextView)this.findViewById(android.R.id.message))
|
|
|
|
.setMovementMethod(LinkMovementMethod.getInstance());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setCallback(OnClickListener callback) {
|
|
|
|
this.callback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class AcceptListener implements OnClickListener {
|
|
|
|
|
|
|
|
private final MasterSecret masterSecret;
|
|
|
|
private final MessageRecord messageRecord;
|
|
|
|
private final IdentityKeyMismatch mismatch;
|
2017-07-26 16:59:15 +00:00
|
|
|
private final Address address;
|
2015-01-15 21:35:35 +00:00
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
|
2015-01-15 21:35:35 +00:00
|
|
|
this.masterSecret = masterSecret;
|
|
|
|
this.messageRecord = messageRecord;
|
|
|
|
this.mismatch = mismatch;
|
2017-07-26 16:59:15 +00:00
|
|
|
this.address = address;
|
2015-01-15 21:35:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
new AsyncTask<Void, Void, Void>()
|
|
|
|
{
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
2017-05-20 01:01:40 +00:00
|
|
|
synchronized (SESSION_LOCK) {
|
2017-07-26 16:59:15 +00:00
|
|
|
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
|
2017-05-31 01:30:37 +00:00
|
|
|
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
2015-01-15 21:35:35 +00:00
|
|
|
|
2017-06-07 01:03:09 +00:00
|
|
|
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
2017-05-20 01:01:40 +00:00
|
|
|
}
|
2017-02-19 20:29:08 +00:00
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
processMessageRecord(messageRecord);
|
|
|
|
processPendingMessageRecords(messageRecord.getThreadId(), mismatch);
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processMessageRecord(MessageRecord messageRecord) {
|
|
|
|
if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord);
|
|
|
|
else processIncomingMessageRecord(messageRecord);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) {
|
|
|
|
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext());
|
|
|
|
Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId);
|
|
|
|
MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor, masterSecret);
|
|
|
|
MessageRecord record;
|
|
|
|
|
|
|
|
try {
|
|
|
|
while ((record = reader.getNext()) != null) {
|
|
|
|
for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) {
|
|
|
|
if (mismatch.equals(recordMismatch)) {
|
|
|
|
processMessageRecord(record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (reader != null)
|
|
|
|
reader.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processOutgoingMessageRecord(MessageRecord messageRecord) {
|
|
|
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
|
|
|
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
|
|
|
|
|
|
|
|
if (messageRecord.isMms()) {
|
|
|
|
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
2017-07-26 16:59:15 +00:00
|
|
|
mismatch.getAddress(),
|
2015-01-15 21:35:35 +00:00
|
|
|
mismatch.getIdentityKey());
|
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
if (messageRecord.getRecipient().isPushGroupRecipient()) {
|
|
|
|
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
|
|
|
|
} else {
|
|
|
|
MessageSender.resend(getContext(), masterSecret, messageRecord);
|
|
|
|
}
|
2015-01-15 21:35:35 +00:00
|
|
|
} else {
|
|
|
|
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
2017-07-26 16:59:15 +00:00
|
|
|
mismatch.getAddress(),
|
2015-01-15 21:35:35 +00:00
|
|
|
mismatch.getIdentityKey());
|
|
|
|
|
|
|
|
MessageSender.resend(getContext(), masterSecret, messageRecord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processIncomingMessageRecord(MessageRecord messageRecord) {
|
|
|
|
try {
|
|
|
|
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
|
|
|
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
|
|
|
|
|
|
|
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
2017-07-26 16:59:15 +00:00
|
|
|
mismatch.getAddress(),
|
2015-01-15 21:35:35 +00:00
|
|
|
mismatch.getIdentityKey());
|
|
|
|
|
2016-11-09 17:37:40 +00:00
|
|
|
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
|
|
|
|
2016-03-23 17:34:41 +00:00
|
|
|
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
2017-07-26 16:59:15 +00:00
|
|
|
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
|
2016-03-23 17:34:41 +00:00
|
|
|
messageRecord.getRecipientDeviceId(), "",
|
|
|
|
messageRecord.getDateSent(),
|
2016-11-09 17:37:40 +00:00
|
|
|
legacy ? Base64.decode(messageRecord.getBody().getBody()) : null,
|
|
|
|
!legacy ? Base64.decode(messageRecord.getBody().getBody()) : null);
|
2015-01-15 21:35:35 +00:00
|
|
|
|
|
|
|
long pushId = pushDatabase.insert(envelope);
|
|
|
|
|
|
|
|
ApplicationContext.getInstance(getContext())
|
|
|
|
.getJobManager()
|
2017-07-26 16:59:15 +00:00
|
|
|
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
|
2015-01-15 21:35:35 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-23 20:03:32 +00:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2015-01-15 21:35:35 +00:00
|
|
|
|
|
|
|
if (callback != null) callback.onClick(null, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class CancelListener implements OnClickListener {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
if (callback != null) callback.onClick(null, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|