refactor: inserting attachments with the messages so that they are linked properly to a mmsID

This commit is contained in:
jubb 2021-03-15 13:35:05 +11:00
parent 323fb75149
commit 817c40b30c
5 changed files with 53 additions and 43 deletions

View File

@ -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.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId 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.linkpreview.LinkPreview
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.threads.Address 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.ecc.ECKeyPair
import org.session.libsignal.libsignal.util.KeyHelper import org.session.libsignal.libsignal.util.KeyHelper
import org.session.libsignal.libsignal.util.guava.Optional 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.SignalServiceAttachmentPointer
import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.api.opengroups.PublicChat import org.session.libsignal.service.loki.api.opengroups.PublicChat
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.attachments.toSignalPointer
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase
@ -94,17 +92,24 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return database.insertAttachments(messageId, databaseAttachments) return database.insertAttachments(messageId, databaseAttachments)
} }
override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?): Long? { override fun getAttachmentsForMessage(messageId: Long): List<DatabaseAttachment> {
val database = DatabaseFactory.getAttachmentDatabase(context)
return database.getAttachmentsForMessage(messageId)
}
override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?, attachments: List<Attachment>): Long? {
var messageID: Long? = null var messageID: Long? = null
val senderAddress = Address.fromSerialized(message.sender!!) val senderAddress = Address.fromSerialized(message.sender!!)
val senderRecipient = Recipient.from(context, senderAddress, false) val senderRecipient = Recipient.from(context, senderAddress, false)
var group: Optional<SignalServiceGroup> = Optional.absent() val group: Optional<SignalServiceGroup> = when {
if (openGroupID != null) { openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT))
group = Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT)) groupPublicKey != null -> Optional.of(SignalServiceGroup(groupPublicKey.toByteArray(), SignalServiceGroup.GroupType.SIGNAL))
} else if (groupPublicKey != null) { else -> Optional.absent()
group = Optional.of(SignalServiceGroup(groupPublicKey.toByteArray(), SignalServiceGroup.GroupType.SIGNAL))
} }
if (message.isMediaMessage()) { val pointerAttachments = attachments.mapNotNull {
it.toSignalAttachment()
}
if (message.isMediaMessage() || attachments.isNotEmpty()) {
val quote: Optional<QuoteModel> = if (quotes != null) Optional.of(quotes) else Optional.absent() val quote: Optional<QuoteModel> = if (quotes != null) Optional.of(quotes) else Optional.absent()
val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! }) val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
val mmsDatabase = DatabaseFactory.getMmsDatabase(context) val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
@ -119,20 +124,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return null return null
} }
} }
val attachments = message.attachmentIDs.mapNotNull {
DatabaseFactory.getAttachmentProvider(context).getSignalAttachmentPointer(it) val mediaMessage = OutgoingMediaMessage.from(message, Recipient.from(context, targetAddress, false), pointerAttachments, quote.orNull(), linkPreviews.orNull().firstOrNull())
}.mapNotNull {
PointerAttachment.forPointer(Optional.of(it)).orNull()
}
val mediaMessage = OutgoingMediaMessage.from(message, Recipient.from(context, targetAddress, false), attachments, quote.orNull(), linkPreviews.orNull().firstOrNull())
mmsDatabase.beginTransaction() mmsDatabase.beginTransaction()
mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!)
} else { } else {
// It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment // It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment
val attachments: Optional<List<SignalServiceAttachment>> = Optional.of(message.attachmentIDs.mapNotNull { val signalServiceAttachments = attachments.mapNotNull {
DatabaseFactory.getAttachmentProvider(context).getAttachmentPointer(it)?.toSignalPointer() it.toSignalPointer()
}) }
val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, attachments, quote, linkPreviews) val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews)
mmsDatabase.beginTransaction() mmsDatabase.beginTransaction()
if (group.isPresent) { if (group.isPresent) {
mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!)

View File

@ -3,10 +3,10 @@ package org.thoughtcrime.securesms.mms;
import org.session.libsession.messaging.messages.visible.VisibleMessage; 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.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment; 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.linkpreview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel; 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.libsession.utilities.GroupUtil;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@ -68,12 +68,12 @@ public class IncomingMediaMessage {
Address from, Address from,
long expiresIn, long expiresIn,
Optional<SignalServiceGroup> group, Optional<SignalServiceGroup> group,
Optional<List<SignalServiceAttachment>> attachments, List<SignalServiceAttachment> attachments,
Optional<QuoteModel> quote, Optional<QuoteModel> quote,
Optional<List<LinkPreview>> linkPreviews) Optional<List<LinkPreview>> linkPreviews)
{ {
return new IncomingMediaMessage(from, message.getReceivedTimestamp(), -1, expiresIn, false, 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() { public int getSubscriptionId() {

View File

@ -6,18 +6,17 @@ import android.net.Uri
import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.MessageSendJob 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.Attachment
import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId 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.linkpreview.LinkPreview
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.GroupRecord
import org.session.libsession.messaging.threads.recipients.Recipient.RecipientSettings import org.session.libsession.messaging.threads.recipients.Recipient.RecipientSettings
import org.session.libsignal.libsignal.ecc.ECKeyPair 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.SignalServiceAttachmentPointer
import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
@ -92,6 +91,7 @@ interface StorageProtocol {
// fun removeReceivedMessageTimestamps(timestamps: Set<Long>) // fun removeReceivedMessageTimestamps(timestamps: Set<Long>)
// Returns the IDs of the saved attachments. // Returns the IDs of the saved attachments.
fun persistAttachments(messageId: Long, attachments: List<Attachment>): List<Long> fun persistAttachments(messageId: Long, attachments: List<Attachment>): List<Long>
fun getAttachmentsForMessage(messageId: Long): List<DatabaseAttachment>
fun getMessageIdInDatabase(timestamp: Long, author: String): Long? fun getMessageIdInDatabase(timestamp: Long, author: String): Long?
fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) fun setOpenGroupServerMessageID(messageID: Long, serverID: Long)
@ -150,5 +150,5 @@ interface StorageProtocol {
// Message Handling // Message Handling
/// Returns the ID of the `TSIncomingMessage` that was constructed. /// Returns the ID of the `TSIncomingMessage` that was constructed.
fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?): Long? fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?, attachments: List<Attachment>): Long?
} }

View File

@ -4,6 +4,7 @@ import android.util.Size
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment 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.api.messages.SignalServiceAttachmentPointer
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Base64
@ -23,7 +24,7 @@ class Attachment {
var url: String? = null var url: String? = null
companion object { companion object {
fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment? { fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment {
val result = Attachment() val result = Attachment()
result.fileName = proto.fileName result.fileName = proto.fileName
fun inferContentType(): String { 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, 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) 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)
}
} }

View File

@ -123,17 +123,6 @@ private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMes
fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) { fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) {
val storage = MessagingConfiguration.shared.storage val storage = MessagingConfiguration.shared.storage
val context = MessagingConfiguration.shared.context 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 // Update profile if needed
val newProfile = message.profile val newProfile = message.profile
if (newProfile != null) { if (newProfile != null) {
@ -187,15 +176,26 @@ 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 // Parse stickers if needed
// Persist the message // 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 message.threadID = threadID
// Parse & persist attachments
// Start attachment downloads if needed // Start attachment downloads if needed
attachmentIDs.forEach { attachmentID -> storage.getAttachmentsForMessage(messageID).forEach { attachment ->
val downloadJob = AttachmentDownloadJob(attachmentID, messageID) attachment.attachmentId?.let { id ->
val downloadJob = AttachmentDownloadJob(id.rowId, messageID)
JobQueue.shared.add(downloadJob) JobQueue.shared.add(downloadJob)
} }
}
// Cancel any typing indicators if needed // Cancel any typing indicators if needed
cancelTypingIndicatorsIfNeeded(message.sender!!) cancelTypingIndicatorsIfNeeded(message.sender!!)
//Notify the user if needed //Notify the user if needed