From 463aaf0fb871944a541714db1a8275365c1e2c5e Mon Sep 17 00:00:00 2001 From: Mikunj Date: Fri, 15 Nov 2019 11:37:36 +1100 Subject: [PATCH] Fix processing of outgoing attachment messages in public chats. Before we were directly inserting messages into the database but that wasn't working because attachments never got downloaded. This fixes it so we forcefully go through signals pipeline via self sync messages. --- .../securesms/jobs/PushDecryptJob.java | 7 +- .../securesms/loki/LokiPublicChatPoller.kt | 135 +++++++----------- 2 files changed, 54 insertions(+), 88 deletions(-) diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index f72f9cf1a5..0c2afbff5e 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -891,7 +891,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { return threadId; } - private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) + public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) throws MmsException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); @@ -927,6 +927,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { try { long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null); + if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } if (recipients.getAddress().isGroup()) { GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); @@ -1222,7 +1223,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } - private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) + public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) throws MmsException { @@ -1245,6 +1246,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType { outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null); + if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } + database = DatabaseFactory.getMmsDatabase(context); GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); diff --git a/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt index 810353a614..cbaa33cf65 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt @@ -4,23 +4,15 @@ import android.content.Context import android.os.Handler import android.util.Log import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.jobs.PushDecryptJob -import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage -import org.thoughtcrime.securesms.mms.QuoteModel -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.Util import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceGroup +import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.loki.api.LokiPublicChat import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI @@ -28,6 +20,7 @@ import org.whispersystems.signalservice.loki.api.LokiPublicChatMessage import org.whispersystems.signalservice.loki.api.LokiStorageAPI import org.whispersystems.signalservice.loki.utilities.get import org.whispersystems.signalservice.loki.utilities.successBackground +import java.util.* class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) { private val handler = Handler() @@ -97,18 +90,17 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki // endregion // region Polling - private fun pollForNewMessages() { - fun processIncomingMessage(message: LokiPublicChatMessage) { - val id = group.id.toByteArray() - val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) - val quote = if (message.quote != null) { - SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) - } else { - null - } - val attachments = message.attachments.mapNotNull { attachment -> - if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } - SignalServiceAttachmentPointer( + private fun getDataMessage(message: LokiPublicChatMessage): SignalServiceDataMessage { + val id = group.id.toByteArray() + val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) + val quote = if (message.quote != null) { + SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) + } else { + null + } + val attachments = message.attachments.mapNotNull { attachment -> + if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } + SignalServiceAttachmentPointer( attachment.serverID, attachment.contentType, ByteArray(0), @@ -120,86 +112,57 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki false, Optional.fromNullable(attachment.caption), attachment.url) - } - val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview } - val signalLinkPreviews = mutableListOf() - if (linkPreview != null) { - val attachment = SignalServiceAttachmentPointer( - linkPreview.serverID, - linkPreview.contentType, - ByteArray(0), - Optional.of(linkPreview.size), - Optional.absent(), - linkPreview.width, linkPreview.height, - Optional.absent(), - Optional.of(linkPreview.fileName), - false, - Optional.fromNullable(linkPreview.caption), - linkPreview.url) - signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) - } - val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body - val serviceDataMessage = SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, false, 0, false, null, false, quote, null, signalLinkPreviews, null) + } + val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview } + val signalLinkPreviews = mutableListOf() + if (linkPreview != null) { + val attachment = SignalServiceAttachmentPointer( + linkPreview.serverID, + linkPreview.contentType, + ByteArray(0), + Optional.of(linkPreview.size), + Optional.absent(), + linkPreview.width, linkPreview.height, + Optional.absent(), + Optional.of(linkPreview.fileName), + false, + Optional.fromNullable(linkPreview.caption), + linkPreview.url) + signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) + } + val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body + return SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, false, 0, false, null, false, quote, null, signalLinkPreviews, null) + } + + private fun pollForNewMessages() { + fun processIncomingMessage(message: LokiPublicChatMessage) { + val serviceDataMessage = getDataMessage(message) val serviceContent = SignalServiceContent(serviceDataMessage, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false) val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) - if (quote != null || attachments.count() > 0 || linkPreview != null) { + if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) } else { PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) } } + fun processOutgoingMessage(message: LokiPublicChatMessage) { val messageServerID = message.serverID ?: return - val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) - val isDuplicate = lokiMessageDatabase.getMessageID(messageServerID) != null + val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null if (isDuplicate) { return } if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } - val id = group.id.toByteArray() - val mmsDatabase = DatabaseFactory.getMmsDatabase(context) - val recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(id, false)), false) - val quote: QuoteModel? - if (message.quote != null) { - quote = QuoteModel(message.quote!!.quotedMessageTimestamp, Address.fromSerialized(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, false, listOf()) + val localNumber = TextSecurePreferences.getLocalNumber(context) + val dataMessage = getDataMessage(message) + val transcript = SentTranscriptMessage(localNumber, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(localNumber, false)) + transcript.messageServerID = messageServerID + if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) { + PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript) } else { - quote = null - } - // TODO: Handle attachments correctly for our previous messages - val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body - val signalMessage = OutgoingMediaMessage(recipient, body, listOf(), message.timestamp, 0, 0, - ThreadDatabase.DistributionTypes.DEFAULT, quote, listOf(), listOf(), listOf(), listOf()) - val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient) - fun finalize() { - val messageID = mmsDatabase.insertMessageOutbox(signalMessage, threadID, false, null) - mmsDatabase.markAsSent(messageID, true) - mmsDatabase.markUnidentified(messageID, false) - lokiMessageDatabase.setServerID(messageID, messageServerID) - } - val urls = LinkPreviewUtil.findWhitelistedUrls(message.body) - val urlCount = urls.size - if (urlCount != 0) { - val lpr = LinkPreviewRepository(context) - var count = 0 - urls.forEach { url -> - lpr.getLinkPreview(context, url.url) { lp -> - Util.runOnMain { - count += 1 - if (lp.isPresent) { signalMessage.linkPreviews.add(lp.get()) } - if (count == urlCount) { - try { - finalize() - } catch (e: Exception) { - // TODO: Handle - } - - } - } - } - } - } else { - finalize() + PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript) } } + api.getMessages(group.channel, group.server).successBackground { messages -> if (messages.isNotEmpty()) { val ourDevices = LokiStorageAPI.shared.getAllDevicePublicKeys(userHexEncodedPublicKey).get(setOf())