diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 1160cf963f..1c5291450f 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -3015,6 +3015,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity messageSender.sendMessage(0, address, Optional.absent(), message); // The message ID doesn't matter DatabaseFactory.getLokiThreadDatabase(context).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.FRIENDS); lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED); + Util.runOnMain(this::updateInputPanel); } catch (Exception e) { Log.d("Loki", "Failed to send background message to: " + contactID + "."); } @@ -3030,6 +3031,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.NONE); String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId).getAddress().toString(); DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID); + updateInputPanel(); } public boolean isFriendsWithAnyLinkedDevice() { diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index d475731b36..209d934aef 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -144,6 +144,7 @@ import javax.inject.Inject; import kotlin.Unit; import network.loki.messenger.R; +import nl.komponents.kovenant.Promise; public class PushDecryptJob extends BaseJob implements InjectableType { @@ -1097,57 +1098,55 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { if (!envelope.isFriendRequest()) { return; } // This handles the case where another user sends us a regular message without authorisation - MultiDeviceUtilitiesKt.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context).success(becomeFriends -> { - if (becomeFriends) { - // Become friends AND update the message they sent - becomeFriendsWithContact(content.getSender()); - // Send them an accept message back + boolean shouldBecomeFriends = MultiDeviceUtilitiesKt.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context); + if (shouldBecomeFriends) { + // Become friends AND update the message they sent + becomeFriendsWithContact(content.getSender()); + // Send them an accept message back + sendBackgroundMessage(content.getSender()); + } else { + // Do regular friend request logic checks + Recipient originalRecipient = getMessageDestination(content, message); + Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message); + LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); + SmsDatabase smsMessageDatabase = DatabaseFactory.getSmsDatabase(context); + MmsDatabase mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context); + LokiMessageDatabase lokiMessageDatabase= DatabaseFactory.getLokiMessageDatabase(context); + + long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(originalRecipient); + long primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDeviceRecipient); + LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID); + int messageCount = smsMessageDatabase.getMessageCountForThread(primaryDeviceThreadID); + if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) { + // This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his + // mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request + // and send a friend request accepted message back to Bob. We don't check that sending the + // friend request accepted message succeeded. Even if it doesn't, the thread's current friend + // request status will be set to `FRIENDS` for Alice making it possible + // for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status + // will then be set to `FRIENDS`. If we do check for a successful send + // before updating Alice's thread's friend request status to `FRIENDS`, + // we can end up in a deadlock where both users' threads' friend request statuses are + // `REQUEST_SENT`. + lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS); + // Since messages are forwarded to the primary device thread, we need to update it there + long messageID = smsMessageDatabase.getIDForMessageAtIndex(primaryDeviceThreadID, messageCount - 2); // The message before the one that was just received + lokiMessageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED); + // Accept the friend request sendBackgroundMessage(content.getSender()); - } else { - // Do regular friend request logic checks - Recipient contactID = getMessageDestination(content, message); - LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); - long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(contactID); - LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID); - SmsDatabase smsMessageDatabase = DatabaseFactory.getSmsDatabase(context); - MmsDatabase mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context); - LokiMessageDatabase lokiMessageDatabase= DatabaseFactory.getLokiMessageDatabase(context); - int messageCount = smsMessageDatabase.getMessageCountForThread(threadID); - if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) { - // This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his - // mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request - // and send a friend request accepted message back to Bob. We don't check that sending the - // friend request accepted message succeeded. Even if it doesn't, the thread's current friend - // request status will be set to `FRIENDS` for Alice making it possible - // for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status - // will then be set to `FRIENDS`. If we do check for a successful send - // before updating Alice's thread's friend request status to `FRIENDS`, - // we can end up in a deadlock where both users' threads' friend request statuses are - // `REQUEST_SENT`. - lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS); - long messageID = smsMessageDatabase.getIDForMessageAtIndex(threadID, messageCount - 2); // The message before the one that was just received - // TODO: MMS - lokiMessageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED); - // Accept the friend request - sendBackgroundMessage(content.getSender()); - } else if (threadFriendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { - // Checking that the sender of the message isn't already a friend is necessary because otherwise - // the following situation can occur: Alice and Bob are friends. Bob loses his database and his - // friend request status is reset to `NONE`. Bob now sends Alice a friend - // request. Alice's thread's friend request status is reset to - // `REQUEST_RECEIVED`. - lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED); - long messageID = smsMessageDatabase.getIDForMessageAtIndex(threadID, messageCount - 1); // The message that was just received - if (messageID != -1) { - lokiMessageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING); - } else { - // TODO: The code below is ugly due to Java limitations - lokiMessageDatabase.setFriendRequestStatus(mmsMessageDatabase.getIDForMessageAtIndex(threadID, 0), LokiMessageFriendRequestStatus.REQUEST_PENDING); - } - } + } else if (threadFriendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { + // Checking that the sender of the message isn't already a friend is necessary because otherwise + // the following situation can occur: Alice and Bob are friends. Bob loses his database and his + // friend request status is reset to `NONE`. Bob now sends Alice a friend + // request. Alice's thread's friend request status is reset to + // `REQUEST_RECEIVED`. + lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED); + // Since messages are forwarded to the primary device thread, we need to update it there + long smsMessageID = smsMessageDatabase.getIDForMessageAtIndex(primaryDeviceThreadID, messageCount - 1); // The message that was just received + long messageID = smsMessageID != -1 ? smsMessageID : mmsMessageDatabase.getIDForMessageAtIndex(primaryDeviceThreadID, 0); + lokiMessageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING); } - return Unit.INSTANCE; - }); + } } private void sendBackgroundMessage(String contactHexEncodedPublicKey) { diff --git a/src/org/thoughtcrime/securesms/loki/LokiPreKeyBundleDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiPreKeyBundleDatabase.kt index becd442e51..978abd6e91 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiPreKeyBundleDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiPreKeyBundleDatabase.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki import android.content.ContentValues import android.content.Context +import net.sqlcipher.Cursor import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.PreKeyUtil import org.thoughtcrime.securesms.database.Database @@ -92,7 +93,14 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : fun hasPreKeyBundle(hexEncodedPublicKey: String): Boolean { val database = databaseHelper.readableDatabase - val cursor = database.query(tableName, null, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey ), null, null, null) - return cursor != null && cursor.count > 0 + var cursor: Cursor? = null + return try { + cursor = database.query(tableName, null, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey ), null, null, null) + cursor != null && cursor.count > 0 + } catch (e: Exception) { + false + } finally { + cursor?.close() + } } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt index 304f72b711..56f534b27b 100644 --- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt @@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.logging.Log import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.concurrent.SettableFuture import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage @@ -51,33 +52,38 @@ fun getAllDevicePublicKeys(context: Context, hexEncodedPublicKey: String, storag } } -fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Context): Promise { +fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Context): Boolean { val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val storageAPI = LokiStorageAPI.shared - val deferred = deferred() + val future = SettableFuture() storageAPI.getPrimaryDevicePublicKey(publicKey).success { primaryDevicePublicKey -> if (primaryDevicePublicKey == null) { - deferred.resolve(false) + future.set(false) return@success } val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) if (primaryDevicePublicKey == userHexEncodedPublicKey) { storageAPI.getSecondaryDevicePublicKeys(userHexEncodedPublicKey).success { secondaryDevices -> - deferred.resolve(secondaryDevices.contains(publicKey)) + future.set(secondaryDevices.contains(publicKey)) }.fail { - deferred.resolve(false) + future.set(false) } return@success } val primaryDevice = Recipient.from(context, Address.fromSerialized(primaryDevicePublicKey), false) val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDevice) if (threadID < 0) { - deferred.resolve(false) + future.set(false) return@success } - deferred.resolve(lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) + future.set(lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) + } + + return try { + future.get() + } catch (e: Exception) { + false } - return deferred.promise } fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise {