diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index b00ed7d2e..3fd91f7f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -186,7 +186,7 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) else DatabaseComponent.get(context).mmsDatabase() messagingDatabase.deleteMessage(messageID) DatabaseComponent.get(context).lokiMessageDatabase().deleteMessage(messageID, isSms) - DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID) + DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID, mms = !isSms) } override fun deleteMessages(messageIDs: List, threadId: Long, isSms: Boolean) { @@ -195,7 +195,7 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) messagingDatabase.deleteMessages(messageIDs.toLongArray(), threadId) DatabaseComponent.get(context).lokiMessageDatabase().deleteMessages(messageIDs) - DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHashes(messageIDs) + DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHashes(messageIDs, mms = !isSms) } override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? { @@ -212,15 +212,12 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) return message.id } - override fun getServerHashForMessage(messageID: Long): String? { - val messageDB = DatabaseComponent.get(context).lokiMessageDatabase() - return messageDB.getMessageServerHash(messageID) - } + override fun getServerHashForMessage(messageID: Long, mms: Boolean): String? = + DatabaseComponent.get(context).lokiMessageDatabase().getMessageServerHash(messageID, mms) - override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? { - val attachmentDatabase = DatabaseComponent.get(context).attachmentDatabase() - return attachmentDatabase.getAttachment(AttachmentId(attachmentId, 0)) - } + override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? = + DatabaseComponent.get(context).attachmentDatabase() + .getAttachment(AttachmentId(attachmentId, 0)) private fun scaleAndStripExif(attachmentDatabase: AttachmentDatabase, constraints: MediaConstraints, attachment: Attachment): Attachment? { return try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 82df37b9f..017e2db68 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1813,7 +1813,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe override fun deleteMessages(messages: Set) { val recipient = viewModel.recipient ?: return val allSentByCurrentUser = messages.all { it.isOutgoing } - val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id) != null } + val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id, it.isMms) != null } if (recipient.isOpenGroupRecipient) { val messageCount = 1 diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/LokiMessageDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/LokiMessageDatabase.kt index 74d473f98..441c97910 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/LokiMessageDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/LokiMessageDatabase.kt @@ -207,52 +207,52 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab messages.add(cursor.getLong(messageID) to cursor.getLong(serverID)) } } - var deletedCount = 0L database.beginTransaction() messages.forEach { (messageId, serverId) -> - deletedCount += database.delete(messageIDTable, "$messageID = ? AND $serverID = ?", arrayOf(messageId.toString(), serverId.toString())) + database.delete(messageIDTable, "$messageID = ? AND $serverID = ?", arrayOf(messageId.toString(), serverId.toString())) } - val mappingDeleted = database.delete(messageThreadMappingTable, "$threadID = ?", arrayOf(threadId.toString())) + database.delete(messageThreadMappingTable, "$threadID = ?", arrayOf(threadId.toString())) database.setTransactionSuccessful() } finally { database.endTransaction() } } - fun getMessageServerHash(messageID: Long): String? { - val database = databaseHelper.readableDatabase - return database.get(messageHashTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor -> + fun getMessageServerHash(messageID: Long, mms: Boolean): String? = getMessageTables(mms).firstNotNullOfOrNull { + databaseHelper.readableDatabase.get(it, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor -> cursor.getString(serverHash) } } - fun setMessageServerHash(messageID: Long, serverHash: String) { - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.messageID, messageID) - contentValues.put(Companion.serverHash, serverHash) - database.insertOrUpdate(messageHashTable, contentValues, "${Companion.messageID} = ?", arrayOf(messageID.toString())) + fun setMessageServerHash(messageID: Long, mms: Boolean, serverHash: String) { + val contentValues = ContentValues(2).apply { + put(Companion.messageID, messageID) + put(Companion.serverHash, serverHash) + } + + databaseHelper.writableDatabase.apply { + insertOrUpdate(getMessageTable(mms), contentValues, "${Companion.messageID} = ?", arrayOf(messageID.toString())) + } } - fun deleteMessageServerHash(messageID: Long) { - val database = databaseHelper.writableDatabase - database.delete(messageHashTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) + fun deleteMessageServerHash(messageID: Long, mms: Boolean) { + getMessageTables(mms).firstOrNull { + databaseHelper.writableDatabase.delete(it, "${Companion.messageID} = ?", arrayOf(messageID.toString())) > 0 + } } - fun deleteMessageServerHashes(messageIDs: List) { - val database = databaseHelper.writableDatabase - database.delete( - messageHashTable, - "${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})", + fun deleteMessageServerHashes(messageIDs: List, mms: Boolean) { + databaseHelper.writableDatabase.delete( + getMessageTable(mms), + "${Companion.messageID} IN (${messageIDs.joinToString(",") { "?" }})", messageIDs.map { "$it" }.toTypedArray() ) } - fun migrateThreadId(legacyThreadId: Long, newThreadId: Long) { - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(1) - contentValues.put(threadID, newThreadId) - database.update(messageThreadMappingTable, contentValues, "$threadID = ?", arrayOf(legacyThreadId.toString())) - } + private fun getMessageTables(mms: Boolean) = sequenceOf( + getMessageTable(mms), + messageHashTable + ) + private fun getMessageTable(mms: Boolean) = if (mms) mmsHashTable else smsHashTable } \ No newline at end of file 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 ded0941fb..f6d3c25ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -376,7 +376,7 @@ open class Storage( } message.serverHash?.let { serverHash -> messageID?.let { id -> - DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(id, serverHash) + DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(id, message.isMediaMessage(), serverHash) } } if (expiryMode is ExpiryMode.AfterSend) { @@ -756,10 +756,10 @@ open class Storage( SessionMetaProtocol.removeTimestamps(timestamps) } - override fun getMessageIdInDatabase(timestamp: Long, author: String): Long? { + override fun getMessageIdInDatabase(timestamp: Long, author: String): Pair? { val database = DatabaseComponent.get(context).mmsSmsDatabase() val address = fromSerialized(author) - return database.getMessageFor(timestamp, address)?.getId() + return database.getMessageFor(timestamp, address)?.run { getId() to isMms } } override fun updateSentTimestamp( @@ -878,8 +878,8 @@ open class Storage( db.clearErrorMessage(messageID) } - override fun setMessageServerHash(messageID: Long, serverHash: String) { - DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(messageID, serverHash) + override fun setMessageServerHash(messageID: Long, mms: Boolean, serverHash: String) { + DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(messageID, mms, serverHash) } override fun getGroup(groupID: String): GroupRecord? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt index 57b616d10..07e14851e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt @@ -61,7 +61,9 @@ class MarkReadReceiver : BroadcastReceiver() { val loki = DatabaseComponent.get(context).lokiMessageDatabase() task { - val hashToInfo = markedReadMessages.associateByNotNull { loki.getMessageServerHash(it.expirationInfo.id) } + val hashToInfo = markedReadMessages.associateByNotNull { + it.expirationInfo.run { loki.getMessageServerHash(id, isMms) } + } if (hashToInfo.isEmpty()) return@task @Suppress("UNCHECKED_CAST") diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index af7295eed..e994ea04f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -202,7 +202,7 @@ class DefaultConversationRepository @Inject constructor( } } else { messageDataProvider.deleteMessage(message.id, !message.isMms) - messageDataProvider.getServerHashForMessage(message.id)?.let { serverHash -> + messageDataProvider.getServerHashForMessage(message.id, message.isMms)?.let { serverHash -> var publicKey = recipient.address.serialize() if (recipient.isClosedGroupRecipient) { publicKey = GroupUtil.doubleDecodeGroupID(publicKey).toHexString() @@ -219,16 +219,15 @@ class DefaultConversationRepository @Inject constructor( override fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? { if (recipient.isOpenGroupRecipient) return null - messageDataProvider.getServerHashForMessage(message.id) ?: return null - val unsendRequest = UnsendRequest() - if (message.isOutgoing) { - unsendRequest.author = textSecurePreferences.getLocalNumber() - } else { - unsendRequest.author = message.individualRecipient.address.contactIdentifier() + messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?: return null + return UnsendRequest().apply { + author = if (message.isOutgoing) { + textSecurePreferences.getLocalNumber() + } else { + message.individualRecipient.address.contactIdentifier() + } + timestamp = message.timestamp } - unsendRequest.timestamp = message.timestamp - - return unsendRequest } override suspend fun deleteMessageWithoutUnsendRequest( @@ -243,7 +242,7 @@ class DefaultConversationRepository @Inject constructor( lokiMessageDb.getServerID(message.id, !message.isMms) ?: continue messageServerIDs[messageServerID] = message } - for ((messageServerID, message) in messageServerIDs) { + messageServerIDs.forEach { (messageServerID, message) -> OpenGroupApi.deleteMessage(messageServerID, openGroup.room, openGroup.server) .success { messageDataProvider.deleteMessage(message.id, !message.isMms) diff --git a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt index 24324ccb7..52c0b8c7a 100644 --- a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt +++ b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt @@ -24,7 +24,7 @@ interface MessageDataProvider { fun deleteMessage(messageID: Long, isSms: Boolean) fun deleteMessages(messageIDs: List, threadId: Long, isSms: Boolean) fun updateMessageAsDeleted(timestamp: Long, author: String): Long? - fun getServerHashForMessage(messageID: Long): String? + fun getServerHashForMessage(messageID: Long, mms: Boolean): String? fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream? fun getAttachmentPointer(attachmentId: Long): SessionServiceAttachmentPointer? 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 3cbf6a116..c9b0f3cf0 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -114,7 +114,7 @@ interface StorageProtocol { */ fun persistAttachments(messageID: Long, attachments: List): List fun getAttachmentsForMessage(messageID: Long): List - fun getMessageIdInDatabase(timestamp: Long, author: String): Long? // TODO: This is a weird name + fun getMessageIdInDatabase(timestamp: Long, author: String): Pair? // TODO: This is a weird name fun updateSentTimestamp(messageID: Long, isMms: Boolean, openGroupSentTimestamp: Long, threadId: Long) fun markAsResyncing(timestamp: Long, author: String) fun markAsSyncing(timestamp: Long, author: String) @@ -124,7 +124,7 @@ interface StorageProtocol { fun markAsSyncFailed(timestamp: Long, author: String, error: Exception) fun markAsSentFailed(timestamp: Long, author: String, error: Exception) fun clearErrorMessage(messageID: Long) - fun setMessageServerHash(messageID: Long, serverHash: String) + fun setMessageServerHash(messageID: Long, mms: Boolean, serverHash: String) // Closed Groups fun getGroup(groupID: String): GroupRecord? diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index 35985cba3..f17a1b2cd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -372,20 +372,23 @@ object MessageSender { fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) { val storage = MessagingModuleConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey()!! + val timestamp = message.sentTimestamp!! // Ignore future self-sends - storage.addReceivedMessageTimestamp(message.sentTimestamp!!) - storage.getMessageIdInDatabase(message.sentTimestamp!!, userPublicKey)?.let { messageID -> + storage.addReceivedMessageTimestamp(timestamp) + storage.getMessageIdInDatabase(timestamp, userPublicKey)?.let { (messageID, mms) -> if (openGroupSentTimestamp != -1L && message is VisibleMessage) { storage.addReceivedMessageTimestamp(openGroupSentTimestamp) storage.updateSentTimestamp(messageID, message.isMediaMessage(), openGroupSentTimestamp, message.threadID!!) message.sentTimestamp = openGroupSentTimestamp } + // When the sync message is successfully sent, the hash value of this TSOutgoingMessage // will be replaced by the hash value of the sync message. Since the hash value of the // real message has no use when we delete a message. It is OK to let it be. message.serverHash?.let { - storage.setMessageServerHash(messageID, it) + storage.setMessageServerHash(messageID, mms, it) } + // in case any errors from previous sends storage.clearErrorMessage(messageID) // Track the open group server message ID @@ -412,11 +415,11 @@ object MessageSender { } } // Mark the message as sent - storage.markAsSent(message.sentTimestamp!!, userPublicKey) - storage.markUnidentified(message.sentTimestamp!!, userPublicKey) + storage.markAsSent(timestamp, userPublicKey) + storage.markUnidentified(timestamp, userPublicKey) // Start the disappearing messages timer if needed if (message.recipient == userPublicKey || !isSyncMessage) { - SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message.sentTimestamp!!, userPublicKey, System.currentTimeMillis()) + SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(timestamp, userPublicKey, System.currentTimeMillis()) } } ?: run { storage.updateReactionIfNeeded(message, message.sender?:userPublicKey, openGroupSentTimestamp) @@ -429,7 +432,7 @@ object MessageSender { if (message is VisibleMessage) message.syncTarget = destination.publicKey if (message is ExpirationTimerUpdate) message.syncTarget = destination.publicKey - storage.markAsSyncing(message.sentTimestamp!!, userPublicKey) + storage.markAsSyncing(timestamp, userPublicKey) sendToSnodeDestination(Destination.Contact(userPublicKey), message, true) } } @@ -456,7 +459,7 @@ object MessageSender { message.linkPreview?.let { linkPreview -> if (linkPreview.attachmentID == null) { messageDataProvider.getLinkPreviewAttachmentIDFor(message.id!!)?.let { attachmentID -> - message.linkPreview!!.attachmentID = attachmentID + linkPreview.attachmentID = attachmentID message.attachmentIDs.remove(attachmentID) } } 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 e5a9c32a0..5d108af0c 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 @@ -259,8 +259,8 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider val timestamp = message.timestamp ?: return null val author = message.author ?: return null - val messageIdToDelete = storage.getMessageIdInDatabase(timestamp, author) ?: return null - messageDataProvider.getServerHashForMessage(messageIdToDelete)?.let { serverHash -> + val (messageIdToDelete, mms) = storage.getMessageIdInDatabase(timestamp, author) ?: return null + messageDataProvider.getServerHashForMessage(messageIdToDelete, mms)?.let { serverHash -> SnodeAPI.deleteMessage(author, listOf(serverHash)) } val deletedMessageId = messageDataProvider.updateMessageAsDeleted(timestamp, author)