diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 6d6ca69670..e792a19df6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1221,11 +1221,6 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co recipientDb.setRecipientHash(recipient, recipientHash) } - override fun getThreadArchived(threadId: Long): Boolean { - val threadDB = DatabaseComponent.get(context).threadDatabase() - return threadDB.getThreadArchived(threadId) - } - override fun getLastUpdated(threadID: Long): Long { val threadDB = DatabaseComponent.get(context).threadDatabase() return threadDB.getLastUpdated(threadID) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index f440ecb644..68b496fb5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -658,24 +658,6 @@ public class ThreadDatabase extends Database { return getOrCreateThreadIdFor(recipient, DistributionTypes.DEFAULT); } - public boolean getThreadArchived(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return (cursor.getInt(cursor.getColumnIndexOrThrow(ARCHIVED)) == 1); - } - } finally { - if (cursor != null) - cursor.close(); - } - - return false; - } - public void setThreadArchived(long threadId) { ContentValues contentValues = new ContentValues(1); contentValues.put(ARCHIVED, 1); diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index 722ad54a3f..c4509df09e 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -166,7 +166,6 @@ interface StorageProtocol { fun getThreadId(address: Address): Long? fun getThreadId(recipient: Recipient): Long? fun getThreadIdForMms(mmsId: Long): Long - fun getThreadArchived(threadId: Long): Boolean fun getLastUpdated(threadID: Long): Long fun trimThread(threadID: Long, threadLimit: Int) fun trimThreadBefore(threadID: Long, timestamp: Long) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 8143a8baa8..bc02db9143 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -61,57 +61,61 @@ internal fun MessageReceiver.isBlocked(publicKey: String): Boolean { } fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, threadId: Long, openGroupID: String?) { + // Do nothing if the message was outdated + if (MessageReceiver.messageIsOutdated(message, threadId, openGroupID)) { return } + when (message) { is ReadReceipt -> handleReadReceipt(message) is TypingIndicator -> handleTypingIndicator(message) is ClosedGroupControlMessage -> handleClosedGroupControlMessage(message) is ExpirationTimerUpdate -> handleExpirationTimerUpdate(message) - is DataExtractionNotification -> handleDataExtractionNotification(message, threadId) + is DataExtractionNotification -> handleDataExtractionNotification(message) is ConfigurationMessage -> handleConfigurationMessage(message) is UnsendRequest -> handleUnsendRequest(message) - is MessageRequestResponse -> handleMessageRequestResponse(message, threadId) + is MessageRequestResponse -> handleMessageRequestResponse(message) is VisibleMessage -> handleVisibleMessage( message, proto, openGroupID, threadId, runThreadUpdate = true, runProfileUpdate = true ) - is CallMessage -> handleCallMessage(message, threadId) + is CallMessage -> handleCallMessage(message) } } +fun MessageReceiver.messageIsOutdated(message: Message, threadId: Long, openGroupID: String?): Boolean { + when (message) { + is ReadReceipt -> return false // No visible artifact created so better to keep for more reliable read states + is UnsendRequest -> return false // We should always process the removal of messages just in case + } + + // Determine the state of the conversation and the validity of the message + val storage = MessagingModuleConfiguration.shared.storage + val userPublicKey = storage.getUserPublicKey()!! + val threadRecipient = storage.getRecipientForThread(threadId) + val conversationVisibleInConfig = storage.conversationInConfig( + if (message.groupPublicKey == null) threadRecipient?.address?.serialize() else null, + message.groupPublicKey, + openGroupID, + true + ) + val canPerformChange = storage.canPerformConfigChange( + if (threadRecipient?.address?.serialize() == userPublicKey) SharedConfigMessage.Kind.USER_PROFILE.name else SharedConfigMessage.Kind.CONTACTS.name, + userPublicKey, + message.sentTimestamp!! + ) + + // If the thread is visible or the message was sent more recently than the last config message (minus + // buffer period) then we should process the message, if not then throw as the message is outdated + return (conversationVisibleInConfig || canPerformChange) +} + // region Control Messages private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) { val context = MessagingModuleConfiguration.shared.context SSKEnvironment.shared.readReceiptManager.processReadReceipts(context, message.sender!!, message.timestamps!!, message.receivedTimestamp!!) } -private fun MessageReceiver.handleCallMessage(message: CallMessage, threadId: Long) { - // Only process the message if the thread is not archived or it was sent after the libSession buffer period - val storage = MessagingModuleConfiguration.shared.storage - val userPublicKey = storage.getUserPublicKey()!! - val recipient = storage.getRecipientForThread(threadId) - val dbThreadIsVisible = ( - threadId > 0 && - recipient != null && - !recipient.isContactRecipient && - !storage.getThreadArchived(threadId) - ) - - if ( - !dbThreadIsVisible && - !storage.conversationInConfig( - recipient?.address?.serialize(), - null, - null, - true - ) && - !storage.canPerformConfigChange( - SharedConfigMessage.Kind.CONTACTS.name, - userPublicKey, - message.sentTimestamp!! - ) - ) { return } - +private fun MessageReceiver.handleCallMessage(message: CallMessage) { // TODO: refactor this out to persistence, just to help debug the flow and send/receive in synchronous testing WebRtcUtils.SIGNAL_QUEUE.trySend(message) } @@ -152,37 +156,12 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer } } -private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification, threadId: Long) { +private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { // We don't handle data extraction messages for groups (they shouldn't be sent, but just in case we filter them here too) if (message.groupPublicKey != null) return val storage = MessagingModuleConfiguration.shared.storage val senderPublicKey = message.sender!! - // Only process the message if the thread is not archived or it was sent after the libSession buffer period - val userPublicKey = storage.getUserPublicKey()!! - val recipient = storage.getRecipientForThread(threadId) - val dbThreadIsVisible = ( - threadId > 0 && - recipient != null && - !recipient.isContactRecipient && - !storage.getThreadArchived(threadId) - ) - - if ( - !dbThreadIsVisible && - !storage.conversationInConfig( - recipient?.address?.serialize(), - null, - null, - true - ) && - !storage.canPerformConfigChange( - if (recipient?.address?.serialize() == userPublicKey) SharedConfigMessage.Kind.USER_PROFILE.name else SharedConfigMessage.Kind.CONTACTS.name, - userPublicKey, - message.sentTimestamp!! - ) - ) { return } - val notification: DataExtractionNotificationInfoMessage = when(message.kind) { is DataExtractionNotification.Kind.Screenshot -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) is DataExtractionNotification.Kind.MediaSaved -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) @@ -267,33 +246,7 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { return deletedMessageId } -fun handleMessageRequestResponse(message: MessageRequestResponse, threadId: Long) { - // Only process the message if the thread is not archived or it was sent after the libSession buffer period - val storage = MessagingModuleConfiguration.shared.storage - val userPublicKey = storage.getUserPublicKey()!! - val recipient = storage.getRecipientForThread(threadId) - val dbThreadIsVisible = ( - threadId > 0 && - recipient != null && - !recipient.isContactRecipient && - !storage.getThreadArchived(threadId) - ) - - if ( - !dbThreadIsVisible && - !storage.conversationInConfig( - recipient?.address?.serialize(), - null, - null, - true - ) && - !storage.canPerformConfigChange( - SharedConfigMessage.Kind.CONTACTS.name, - userPublicKey, - message.sentTimestamp!! - ) - ) { return } - +fun handleMessageRequestResponse(message: MessageRequestResponse) { MessagingModuleConfiguration.shared.storage.insertMessageRequestResponse(message) } //endregion @@ -308,32 +261,11 @@ fun MessageReceiver.handleVisibleMessage( ): Long? { val storage = MessagingModuleConfiguration.shared.storage val context = MessagingModuleConfiguration.shared.context - val userPublicKey = storage.getUserPublicKey()!! + val userPublicKey = storage.getUserPublicKey() val messageSender: String? = message.sender - // Only process the message if the thread is not archived or it was sent after the libSession buffer period - val threadRecipient = storage.getRecipientForThread(threadId) - val dbThreadIsVisible = ( - threadId > 0 && - threadRecipient != null && - !threadRecipient.isContactRecipient && - !storage.getThreadArchived(threadId) - ) - - if ( - !dbThreadIsVisible && - !storage.conversationInConfig( - if (message.groupPublicKey == null) threadRecipient?.address?.serialize() else null, - message.groupPublicKey, - openGroupID, - true - ) && - !storage.canPerformConfigChange( - if (threadRecipient?.address?.serialize() == userPublicKey) SharedConfigMessage.Kind.USER_PROFILE.name else SharedConfigMessage.Kind.CONTACTS.name, - userPublicKey, - message.sentTimestamp!! - ) - ) { return null } + // Do nothing if the message was outdated + if (MessageReceiver.messageIsOutdated(message, threadId, openGroupID)) { return null } // Get or create thread // FIXME: In case this is an open group this actually * doesn't * create the thread if it doesn't yet @@ -341,6 +273,7 @@ fun MessageReceiver.handleVisibleMessage( val threadID = storage.getThreadIdFor(message.syncTarget ?: messageSender!!, message.groupPublicKey, openGroupID, createThread = true) // Thread doesn't exist; should only be reached in a case where we are processing open group messages for a no longer existent thread ?: throw MessageReceiver.Error.NoThread + val threadRecipient = storage.getRecipientForThread(threadID) val userBlindedKey = openGroupID?.let { val openGroup = storage.getOpenGroup(threadID) ?: return@let null val blindedKey = SodiumUtilities.blindedKeyPair(openGroup.publicKey, MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!) ?: return@let null