From 817c40b30ccb2e2fccae92e774ebc473f371050a Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 15 Mar 2021 13:35:05 +1100 Subject: [PATCH] refactor: inserting attachments with the messages so that they are linked properly to a mmsID --- .../securesms/database/Storage.kt | 41 ++++++++++--------- .../securesms/mms/IncomingMediaMessage.java | 8 ++-- .../libsession/messaging/StorageProtocol.kt | 6 +-- .../messaging/messages/visible/Attachment.kt | 11 ++++- .../MessageReceiverHandler.kt | 30 +++++++------- 5 files changed, 53 insertions(+), 43 deletions(-) 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 853fc3debe..3b70783d9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -15,7 +15,7 @@ import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId -import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment +import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.threads.Address @@ -27,13 +27,11 @@ import org.session.libsession.utilities.preferences.ProfileKeyUtil import org.session.libsignal.libsignal.ecc.ECKeyPair import org.session.libsignal.libsignal.util.KeyHelper import org.session.libsignal.libsignal.util.guava.Optional -import org.session.libsignal.service.api.messages.SignalServiceAttachment import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.loki.api.opengroups.PublicChat import org.session.libsignal.utilities.logging.Log -import org.thoughtcrime.securesms.attachments.toSignalPointer import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase @@ -94,17 +92,24 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return database.insertAttachments(messageId, databaseAttachments) } - override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?): Long? { + override fun getAttachmentsForMessage(messageId: Long): List { + val database = DatabaseFactory.getAttachmentDatabase(context) + return database.getAttachmentsForMessage(messageId) + } + + override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?, attachments: List): Long? { var messageID: Long? = null val senderAddress = Address.fromSerialized(message.sender!!) val senderRecipient = Recipient.from(context, senderAddress, false) - var group: Optional = Optional.absent() - if (openGroupID != null) { - group = Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT)) - } else if (groupPublicKey != null) { - group = Optional.of(SignalServiceGroup(groupPublicKey.toByteArray(), SignalServiceGroup.GroupType.SIGNAL)) + val group: Optional = when { + openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT)) + groupPublicKey != null -> Optional.of(SignalServiceGroup(groupPublicKey.toByteArray(), SignalServiceGroup.GroupType.SIGNAL)) + else -> Optional.absent() } - if (message.isMediaMessage()) { + val pointerAttachments = attachments.mapNotNull { + it.toSignalAttachment() + } + if (message.isMediaMessage() || attachments.isNotEmpty()) { val quote: Optional = if (quotes != null) Optional.of(quotes) else Optional.absent() val linkPreviews: Optional> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! }) val mmsDatabase = DatabaseFactory.getMmsDatabase(context) @@ -119,20 +124,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return null } } - val attachments = message.attachmentIDs.mapNotNull { - DatabaseFactory.getAttachmentProvider(context).getSignalAttachmentPointer(it) - }.mapNotNull { - PointerAttachment.forPointer(Optional.of(it)).orNull() - } - val mediaMessage = OutgoingMediaMessage.from(message, Recipient.from(context, targetAddress, false), attachments, quote.orNull(), linkPreviews.orNull().firstOrNull()) + + val mediaMessage = OutgoingMediaMessage.from(message, Recipient.from(context, targetAddress, false), pointerAttachments, quote.orNull(), linkPreviews.orNull().firstOrNull()) mmsDatabase.beginTransaction() mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) } else { // It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment - val attachments: Optional> = Optional.of(message.attachmentIDs.mapNotNull { - DatabaseFactory.getAttachmentProvider(context).getAttachmentPointer(it)?.toSignalPointer() - }) - val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, attachments, quote, linkPreviews) + val signalServiceAttachments = attachments.mapNotNull { + it.toSignalPointer() + } + val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews) mmsDatabase.beginTransaction() if (group.isPresent) { mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java index 7c1451056f..a709efa339 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -3,10 +3,10 @@ package org.thoughtcrime.securesms.mms; import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment; -import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact; -import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview; import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel; +import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact; +import org.session.libsession.messaging.threads.Address; import org.session.libsession.utilities.GroupUtil; import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.service.api.messages.SignalServiceAttachment; @@ -68,12 +68,12 @@ public class IncomingMediaMessage { Address from, long expiresIn, Optional group, - Optional> attachments, + List attachments, Optional quote, Optional> linkPreviews) { return new IncomingMediaMessage(from, message.getReceivedTimestamp(), -1, expiresIn, false, - false, Optional.fromNullable(message.getText()), group, attachments, quote, Optional.absent(), linkPreviews); + false, Optional.fromNullable(message.getText()), group, Optional.fromNullable(attachments), quote, Optional.absent(), linkPreviews); } public int getSubscriptionId() { diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index a7bb7e0dc1..c4535f5055 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -6,18 +6,17 @@ import android.net.Uri import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.MessageSendJob -import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId +import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient.RecipientSettings import org.session.libsignal.libsignal.ecc.ECKeyPair -import org.session.libsignal.libsignal.ecc.ECPrivateKey import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -92,6 +91,7 @@ interface StorageProtocol { // fun removeReceivedMessageTimestamps(timestamps: Set) // Returns the IDs of the saved attachments. fun persistAttachments(messageId: Long, attachments: List): List + fun getAttachmentsForMessage(messageId: Long): List fun getMessageIdInDatabase(timestamp: Long, author: String): Long? fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) @@ -150,5 +150,5 @@ interface StorageProtocol { // Message Handling /// Returns the ID of the `TSIncomingMessage` that was constructed. - fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?): Long? + fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?, attachments: List): Long? } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt index 1e4e669284..24226c7274 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt @@ -4,6 +4,7 @@ import android.util.Size import android.webkit.MimeTypeMap import com.google.protobuf.ByteString import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment +import org.session.libsignal.libsignal.util.guava.Optional import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.utilities.Base64 @@ -23,7 +24,7 @@ class Attachment { var url: String? = null companion object { - fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment? { + fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment { val result = Attachment() result.fileName = proto.fileName fun inferContentType(): String { @@ -104,4 +105,12 @@ class Attachment { sizeInBytes?.toLong() ?: 0, if (fileName.isNullOrEmpty()) null else fileName, null, Base64.encodeBytes(key), null, digest, null, kind == Kind.VOICE_MESSAGE, size?.width ?: 0, size?.height ?: 0, false, caption, url) } + + fun toSignalPointer(): SignalServiceAttachmentPointer? { + if (!isValid()) return null + return SignalServiceAttachmentPointer(0, contentType, key, Optional.fromNullable(sizeInBytes), null, + size?.width ?: 0, size?.height ?: 0, Optional.fromNullable(digest), Optional.fromNullable(fileName), + kind == Kind.VOICE_MESSAGE, Optional.fromNullable(caption), url) + } + } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 10c0420c12..e3718534f8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -123,17 +123,6 @@ private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMes fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) { val storage = MessagingConfiguration.shared.storage val context = MessagingConfiguration.shared.context - // Parse & persist attachments - val attachments = proto.dataMessage.attachmentsList.mapNotNull { proto -> - val attachment = Attachment.fromProto(proto) - if (attachment == null || !attachment.isValid()) { - return@mapNotNull null - } else { - return@mapNotNull attachment - } - } - val attachmentIDs = storage.persistAttachments(message.id ?: 0, attachments) - message.attachmentIDs.addAll(attachmentIDs.toMutableList()) // Update profile if needed val newProfile = message.profile if (newProfile != null) { @@ -187,14 +176,25 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS } } } + val attachments = proto.dataMessage.attachmentsList.mapNotNull { proto -> + val attachment = Attachment.fromProto(proto) + if (!attachment.isValid()) { + return@mapNotNull null + } else { + return@mapNotNull attachment + } + } // Parse stickers if needed // Persist the message - val messageID = storage.persist(message, quoteModel, linkPreviews, message.groupPublicKey, openGroupID) ?: throw MessageReceiver.Error.NoThread + val messageID = storage.persist(message, quoteModel, linkPreviews, message.groupPublicKey, openGroupID, attachments) ?: throw MessageReceiver.Error.NoThread message.threadID = threadID + // Parse & persist attachments // Start attachment downloads if needed - attachmentIDs.forEach { attachmentID -> - val downloadJob = AttachmentDownloadJob(attachmentID, messageID) - JobQueue.shared.add(downloadJob) + storage.getAttachmentsForMessage(messageID).forEach { attachment -> + attachment.attachmentId?.let { id -> + val downloadJob = AttachmentDownloadJob(id.rowId, messageID) + JobQueue.shared.add(downloadJob) + } } // Cancel any typing indicators if needed cancelTypingIndicatorsIfNeeded(message.sender!!)