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.
This commit is contained in:
Mikunj 2019-11-15 11:37:36 +11:00
parent 848cab8677
commit 463aaf0fb8
2 changed files with 54 additions and 88 deletions

View File

@ -891,7 +891,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return threadId; return threadId;
} }
private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@ -927,6 +927,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
try { try {
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null); long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null);
if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); }
if (recipients.getAddress().isGroup()) { if (recipients.getAddress().isGroup()) {
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); 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 throws MmsException
{ {
@ -1245,6 +1246,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage);
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null); messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null);
if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); }
database = DatabaseFactory.getMmsDatabase(context); database = DatabaseFactory.getMmsDatabase(context);
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);

View File

@ -4,23 +4,15 @@ import android.content.Context
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.jobs.PushDecryptJob 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.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.messages.SignalServiceGroup 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.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.LokiPublicChat import org.whispersystems.signalservice.loki.api.LokiPublicChat
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI 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.api.LokiStorageAPI
import org.whispersystems.signalservice.loki.utilities.get import org.whispersystems.signalservice.loki.utilities.get
import org.whispersystems.signalservice.loki.utilities.successBackground import org.whispersystems.signalservice.loki.utilities.successBackground
import java.util.*
class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) { class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) {
private val handler = Handler() private val handler = Handler()
@ -97,18 +90,17 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
// endregion // endregion
// region Polling // region Polling
private fun pollForNewMessages() { private fun getDataMessage(message: LokiPublicChatMessage): SignalServiceDataMessage {
fun processIncomingMessage(message: LokiPublicChatMessage) { val id = group.id.toByteArray()
val id = group.id.toByteArray() val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) val quote = if (message.quote != null) {
val quote = if (message.quote != null) { SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf())
SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) } else {
} else { null
null }
} val attachments = message.attachments.mapNotNull { attachment ->
val attachments = message.attachments.mapNotNull { attachment -> if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null }
if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } SignalServiceAttachmentPointer(
SignalServiceAttachmentPointer(
attachment.serverID, attachment.serverID,
attachment.contentType, attachment.contentType,
ByteArray(0), ByteArray(0),
@ -120,86 +112,57 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
false, false,
Optional.fromNullable(attachment.caption), Optional.fromNullable(attachment.caption),
attachment.url) attachment.url)
} }
val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview } val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview }
val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>() val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>()
if (linkPreview != null) { if (linkPreview != null) {
val attachment = SignalServiceAttachmentPointer( val attachment = SignalServiceAttachmentPointer(
linkPreview.serverID, linkPreview.serverID,
linkPreview.contentType, linkPreview.contentType,
ByteArray(0), ByteArray(0),
Optional.of(linkPreview.size), Optional.of(linkPreview.size),
Optional.absent(), Optional.absent(),
linkPreview.width, linkPreview.height, linkPreview.width, linkPreview.height,
Optional.absent(), Optional.absent(),
Optional.of(linkPreview.fileName), Optional.of(linkPreview.fileName),
false, false,
Optional.fromNullable(linkPreview.caption), Optional.fromNullable(linkPreview.caption),
linkPreview.url) linkPreview.url)
signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) 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 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) 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 serviceContent = SignalServiceContent(serviceDataMessage, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) 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)) PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else { } else {
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} }
} }
fun processOutgoingMessage(message: LokiPublicChatMessage) { fun processOutgoingMessage(message: LokiPublicChatMessage) {
val messageServerID = message.serverID ?: return val messageServerID = message.serverID ?: return
val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
val isDuplicate = lokiMessageDatabase.getMessageID(messageServerID) != null
if (isDuplicate) { return } if (isDuplicate) { return }
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
val id = group.id.toByteArray() val localNumber = TextSecurePreferences.getLocalNumber(context)
val mmsDatabase = DatabaseFactory.getMmsDatabase(context) val dataMessage = getDataMessage(message)
val recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(id, false)), false) val transcript = SentTranscriptMessage(localNumber, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(localNumber, false))
val quote: QuoteModel? transcript.messageServerID = messageServerID
if (message.quote != null) { if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
quote = QuoteModel(message.quote!!.quotedMessageTimestamp, Address.fromSerialized(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, false, listOf()) PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript)
} else { } else {
quote = null PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
}
// 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()
} }
} }
api.getMessages(group.channel, group.server).successBackground { messages -> api.getMessages(group.channel, group.server).successBackground { messages ->
if (messages.isNotEmpty()) { if (messages.isNotEmpty()) {
val ourDevices = LokiStorageAPI.shared.getAllDevicePublicKeys(userHexEncodedPublicKey).get(setOf()) val ourDevices = LokiStorageAPI.shared.getAllDevicePublicKeys(userHexEncodedPublicKey).get(setOf())