From 3c328b3c8464bf64bc2cba6a88ee62f160cdc802 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 14 May 2020 11:19:20 +1000 Subject: [PATCH] Add missing friend request protocol logic --- .../conversation/ConversationActivity.java | 37 ++++++---- .../loki/protocol/FriendRequestProtocol.kt | 67 ++++++++++++++++++- .../protocol/SessionManagementProtocol.kt | 20 +++++- .../loki/protocol/SyncMessagesProtocol.kt | 5 +- 4 files changed, 109 insertions(+), 20 deletions(-) diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index e8729d1d15..d1c9208dea 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -139,7 +139,6 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; -import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.identity.IdentityRecordList; import org.thoughtcrime.securesms.database.model.MessageRecord; @@ -155,13 +154,13 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; @@ -215,7 +214,6 @@ import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.ExpirationUtil; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -232,9 +230,7 @@ import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.protocol.mentions.Mention; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; -import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol; -import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; @@ -243,7 +239,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -456,7 +451,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity }); sessionRestoreBannerView.setOnRestore(() -> { - this.restoreSession(); + SessionManagementProtocol.startSessionReset(this, recipient, threadId); + updateSessionRestoreBanner(); return Unit.INSTANCE; }); sessionRestoreBannerView.setOnDismiss(() -> { @@ -1153,7 +1149,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); builder.setPositiveButton(R.string.yes, (dialog, which) -> { Recipient groupRecipient = getRecipient(); - if (GroupUtil.leaveGroup(this, groupRecipient)) { + if (ClosedGroupsProtocol.leaveGroup(this, groupRecipient)) { initializeEnabledCheck(); } else { Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); @@ -2276,7 +2272,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void updateInputPanel() { - boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(threadId); + boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, threadId); Util.runOnMain(() -> { updateToggleButtonState(); String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request"; @@ -2476,7 +2472,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void updateToggleButtonState() { - if (!FriendRequestProtocol.shouldAttachmentsButtonBeEnabled(threadId)) { + if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, threadId)) { buttonToggle.display(sendButton); quickAttachmentToggle.hide(); inlineAttachmentToggle.hide(); @@ -2872,7 +2868,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this); LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this); if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { - List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates("", threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); + List mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId); currentMentionStartIndex = lastCharacterIndex; mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionView.show(mentionCandidates, threadId); @@ -2883,7 +2879,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else { if (currentMentionStartIndex != -1) { String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ - List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates(query, threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); + List mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionView.show(mentionCandidates, threadId); } @@ -3190,5 +3186,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity updateSubtitleTextView(); updateMessageStatusProgressBar(); } + + @Override + public void acceptFriendRequest(@NotNull MessageRecord friendRequest) { + if (recipient.isGroupRecipient()) { return; } + FriendRequestProtocol.acceptFriendRequest(this, recipient); + updateInputPanel(); + } + + @Override + public void rejectFriendRequest(@NotNull MessageRecord friendRequest) { + if (recipient.isGroupRecipient()) { return; } + FriendRequestProtocol.rejectFriendRequest(this, recipient); + updateInputPanel(); + } + // endregion } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/FriendRequestProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/FriendRequestProtocol.kt index f7e78fbfc5..f7114ab908 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/FriendRequestProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/FriendRequestProtocol.kt @@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.mms.OutgoingMediaMessage +import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.sms.OutgoingTextMessage import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.signalservice.api.messages.SignalServiceContent @@ -16,12 +17,72 @@ import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendReque object FriendRequestProtocol { @JvmStatic - fun acceptFriendRequest(context: Context, contactPublicKey: String) { + fun acceptFriendRequest(context: Context, recipient: Recipient) { + if (recipient.isGroupRecipient) { return; } + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) + // Accept all outstanding friend requests associated with this user and try to establish sessions with the + // subset of their devices that haven't sent a friend request. + val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize()) + val threadDB = DatabaseFactory.getThreadDatabase(context) + val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) + for (device in linkedDevices) { + val deviceAsRecipient = recipient(context, device) + val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient) + val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID) + if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { + lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.FRIENDS) + val lastMessageID = getLastMessageID(context, deviceThreadID) + if (lastMessageID != null) { + DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) + } + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, device), true) + val ephemeralMessage = EphemeralMessage.create(device) + ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage)) + // Sync contact if needed + if (allUserDevices.contains(device)) { return } + val deviceToSync = MultiDeviceProtocol.shared.getMasterDevice(device) ?: device + SyncMessagesProtocol.syncContact(context, Address.fromSerialized(deviceToSync)) + } else if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) { + // Do nothing + } else if (!allUserDevices.contains(device) + && (deviceFRStatus == LokiThreadFriendRequestStatus.NONE || deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED)) { + // TODO: Send AFR to contact (NOT their linked devices) + } + } + } + + @JvmStatic + fun rejectFriendRequest(context: Context, recipient: Recipient) { + if (recipient.isGroupRecipient) { return; } + val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize()) + val threadDB = DatabaseFactory.getThreadDatabase(context) + val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) + for (device in linkedDevices) { + val deviceAsRecipient = recipient(context, device) + val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient) + val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID) + // We only want to decline incoming requests + if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { + // Delete the pre key bundle for the given contact. This ensures that if we send a + // new message after this, it restarts the friend request process from scratch. + DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(device) + lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.NONE) + val lastMessageID = getLastMessageID(context, deviceThreadID) + if (lastMessageID != null) { + DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_REJECTED) + } + } + } + } + + @JvmStatic + fun shouldInputPanelBeEnabled(context: Context, threadID: Long): Boolean { } @JvmStatic - fun rejectFriendRequest(context: Context, contactPublicKey: String) { + fun shouldAttachmentButtonBeEnabled(context: Context, threadID: Long): Boolean { } @@ -51,6 +112,7 @@ object FriendRequestProtocol { if (lastMessageID != null) { DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) } + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true) // Send a contact sync message if needed val userPublicKey = TextSecurePreferences.getLocalNumber(context) val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) @@ -107,6 +169,7 @@ object FriendRequestProtocol { if (lastMessageID != null) { DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) } + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true) val ephemeralMessage = EphemeralMessage.create(publicKey) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage)) } else if (threadFRStatus != LokiThreadFriendRequestStatus.FRIENDS) { diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt index a7234253d1..aed9bf0955 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt @@ -10,17 +10,31 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.jobs.CleanPreKeysJob import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.sms.OutgoingTextMessage import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus - object SessionManagementProtocol { @JvmStatic - fun startSessionReset(context: Context, contactPublicKey: String) { - + fun startSessionReset(context: Context, recipient: Recipient, threadID: Long) { + if (recipient.isGroupRecipient) { return } + val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) + val smsDB = DatabaseFactory.getSmsDatabase(context) + val devices = lokiThreadDB.getSessionRestoreDevices(threadID) + for (device in devices) { + val sessionRestorationRequest = EphemeralMessage.createSessionRestorationRequest(recipient.address.serialize()) + ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRestorationRequest)) + } + val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) + val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) + if (infoMessageID > -1) { + smsDB.markAsSentLokiSessionRestorationRequest(infoMessageID) + } + lokiThreadDB.removeAllSessionRestoreDevices(threadID) } @JvmStatic diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt index 3f0bc95fed..20abf3471c 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt @@ -104,15 +104,16 @@ object SyncMessagesProtocol { val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID) when (threadFRStatus) { LokiThreadFriendRequestStatus.NONE, LokiThreadFriendRequestStatus.REQUEST_EXPIRED -> { - // TODO: Send AFR to contact and their linked devices + // TODO: Send AFR to contact AND THEIR LINKED DEVICES } LokiThreadFriendRequestStatus.REQUEST_RECEIVED -> { - FriendRequestProtocol.acceptFriendRequest(contactPublicKey) // Takes into account multi device internally + FriendRequestProtocol.acceptFriendRequest(context, recipient(context, contactPublicKey)) // Takes into account multi device internally lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS) val lastMessageID = FriendRequestProtocol.getLastMessageID(context, threadID) if (lastMessageID != null) { DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) } + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, contactPublicKey), true) } else -> { // Do nothing