mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 18:00:02 +01:00
Convert SignalService, Database, Group, Payment, and other remaining protos to wire.
This commit is contained in:
committed by
Alex Hart
parent
a6b7d0bcc5
commit
efbd5cab85
@@ -4,6 +4,7 @@ import org.signal.ringrtc.CallId
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.warn
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer
|
||||
@@ -18,49 +19,56 @@ import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.api.messages.calls.HangupMessage
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Opaque
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.CallMessage
|
||||
import org.whispersystems.signalservice.internal.push.CallMessage.Offer
|
||||
import org.whispersystems.signalservice.internal.push.CallMessage.Opaque
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
object CallMessageProcessor {
|
||||
fun process(
|
||||
senderRecipient: Recipient,
|
||||
envelope: Envelope,
|
||||
content: SignalServiceProtos.Content,
|
||||
content: Content,
|
||||
metadata: EnvelopeMetadata,
|
||||
serverDeliveredTimestamp: Long
|
||||
) {
|
||||
val callMessage = content.callMessage
|
||||
val callMessage = content.callMessage!!
|
||||
|
||||
when {
|
||||
callMessage.hasOffer() -> handleCallOfferMessage(envelope, metadata, callMessage.offer, senderRecipient.id, serverDeliveredTimestamp, callMessage.multiRing)
|
||||
callMessage.hasAnswer() -> handleCallAnswerMessage(envelope, metadata, callMessage.answer, senderRecipient.id, callMessage.multiRing)
|
||||
callMessage.iceUpdateList.isNotEmpty() -> handleCallIceUpdateMessage(envelope, metadata, callMessage.iceUpdateList, senderRecipient.id)
|
||||
callMessage.hasHangup() || callMessage.hasLegacyHangup() -> {
|
||||
val hangup = if (callMessage.hasHangup()) callMessage.hangup else callMessage.legacyHangup
|
||||
handleCallHangupMessage(envelope, metadata, hangup, senderRecipient.id)
|
||||
callMessage.offer != null -> handleCallOfferMessage(envelope, metadata, callMessage.offer!!, senderRecipient.id, serverDeliveredTimestamp)
|
||||
callMessage.answer != null -> handleCallAnswerMessage(envelope, metadata, callMessage.answer!!, senderRecipient.id)
|
||||
callMessage.iceUpdate.isNotEmpty() -> handleCallIceUpdateMessage(envelope, metadata, callMessage.iceUpdate, senderRecipient.id)
|
||||
callMessage.hangup != null || callMessage.legacyHangup != null -> {
|
||||
handleCallHangupMessage(envelope, metadata, callMessage.hangup ?: callMessage.legacyHangup, senderRecipient.id)
|
||||
}
|
||||
callMessage.hasBusy() -> handleCallBusyMessage(envelope, metadata, callMessage.busy, senderRecipient.id)
|
||||
callMessage.hasOpaque() -> handleCallOpaqueMessage(envelope, metadata, callMessage.opaque, senderRecipient.requireAci(), serverDeliveredTimestamp)
|
||||
callMessage.busy != null -> handleCallBusyMessage(envelope, metadata, callMessage.busy!!, senderRecipient.id)
|
||||
callMessage.opaque != null -> handleCallOpaqueMessage(envelope, metadata, callMessage.opaque!!, senderRecipient.requireAci(), serverDeliveredTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCallOfferMessage(envelope: Envelope, metadata: EnvelopeMetadata, offer: Offer, senderRecipientId: RecipientId, serverDeliveredTimestamp: Long, multiRing: Boolean) {
|
||||
log(envelope.timestamp, "handleCallOfferMessage...")
|
||||
private fun handleCallOfferMessage(envelope: Envelope, metadata: EnvelopeMetadata, offer: Offer, senderRecipientId: RecipientId, serverDeliveredTimestamp: Long) {
|
||||
log(envelope.timestamp!!, "handleCallOfferMessage...")
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(offer.id))
|
||||
val offerId = if (offer.id != null && offer.type != null && ((offer.opaque != null) xor (offer.sdp != null))) {
|
||||
offer.id!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid offer, missing id/type, or invalid combination of opaque/sdp")
|
||||
return
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(offerId))
|
||||
val remoteIdentityKey = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()
|
||||
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedOffer(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
offer.toCallOfferMetadata(),
|
||||
OfferMetadata(offer.opaque?.toByteArray(), offer.sdp, OfferMessage.Type.fromProto(offer.type!!)),
|
||||
ReceivedOfferMetadata(
|
||||
remoteIdentityKey,
|
||||
envelope.serverTimestamp,
|
||||
serverDeliveredTimestamp,
|
||||
multiRing
|
||||
envelope.serverTimestamp!!,
|
||||
serverDeliveredTimestamp
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -68,94 +76,119 @@ object CallMessageProcessor {
|
||||
private fun handleCallAnswerMessage(
|
||||
envelope: Envelope,
|
||||
metadata: EnvelopeMetadata,
|
||||
answer: SignalServiceProtos.CallMessage.Answer,
|
||||
senderRecipientId: RecipientId,
|
||||
multiRing: Boolean
|
||||
answer: CallMessage.Answer,
|
||||
senderRecipientId: RecipientId
|
||||
) {
|
||||
log(envelope.timestamp, "handleCallAnswerMessage...")
|
||||
log(envelope.timestamp!!, "handleCallAnswerMessage...")
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(answer.id))
|
||||
val answerId = if (answer.id != null && ((answer.opaque != null) xor (answer.sdp != null))) {
|
||||
answer.id!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid answer, missing id")
|
||||
return
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(answerId))
|
||||
val remoteIdentityKey = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()
|
||||
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedAnswer(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
AnswerMetadata(if (answer.hasOpaque()) answer.opaque.toByteArray() else null, if (answer.hasSdp()) answer.sdp else null),
|
||||
ReceivedAnswerMetadata(remoteIdentityKey, multiRing)
|
||||
AnswerMetadata(answer.opaque?.toByteArray(), answer.sdp),
|
||||
ReceivedAnswerMetadata(remoteIdentityKey)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleCallIceUpdateMessage(
|
||||
envelope: Envelope,
|
||||
metadata: EnvelopeMetadata,
|
||||
iceUpdateList: MutableList<SignalServiceProtos.CallMessage.IceUpdate>,
|
||||
iceUpdateList: List<CallMessage.IceUpdate>,
|
||||
senderRecipientId: RecipientId
|
||||
) {
|
||||
log(envelope.timestamp, "handleCallIceUpdateMessage... " + iceUpdateList.size)
|
||||
log(envelope.timestamp!!, "handleCallIceUpdateMessage... " + iceUpdateList.size)
|
||||
|
||||
val iceCandidates: MutableList<ByteArray> = ArrayList(iceUpdateList.size)
|
||||
var callId: Long = -1
|
||||
|
||||
iceUpdateList
|
||||
.filter { it.hasOpaque() }
|
||||
.filter { it.opaque != null && it.id != null }
|
||||
.forEach { iceUpdate ->
|
||||
iceCandidates += iceUpdate.opaque.toByteArray()
|
||||
callId = iceUpdate.id
|
||||
iceCandidates += iceUpdate.opaque!!.toByteArray()
|
||||
callId = iceUpdate.id!!
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(callId))
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedIceCandidates(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
iceCandidates
|
||||
)
|
||||
if (iceCandidates.isNotEmpty()) {
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(callId))
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedIceCandidates(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
iceCandidates
|
||||
)
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid ice updates, all missing opaque and/or call id")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCallHangupMessage(
|
||||
envelope: Envelope,
|
||||
metadata: EnvelopeMetadata,
|
||||
hangup: SignalServiceProtos.CallMessage.Hangup,
|
||||
hangup: CallMessage.Hangup?,
|
||||
senderRecipientId: RecipientId
|
||||
) {
|
||||
log(envelope.timestamp, "handleCallHangupMessage")
|
||||
log(envelope.timestamp!!, "handleCallHangupMessage")
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(hangup.id))
|
||||
val (hangupId, hangupDeviceId) = if (hangup?.id != null && hangup.deviceId != null) {
|
||||
hangup.id!! to hangup.deviceId!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid hangup, null message or missing id/deviceId")
|
||||
return
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(hangupId))
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedCallHangup(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
HangupMetadata(HangupMessage.Type.fromProto(hangup.type), hangup.deviceId)
|
||||
HangupMetadata(HangupMessage.Type.fromProto(hangup.type), hangupDeviceId)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleCallBusyMessage(envelope: Envelope, metadata: EnvelopeMetadata, busy: SignalServiceProtos.CallMessage.Busy, senderRecipientId: RecipientId) {
|
||||
log(envelope.timestamp, "handleCallBusyMessage")
|
||||
private fun handleCallBusyMessage(envelope: Envelope, metadata: EnvelopeMetadata, busy: CallMessage.Busy, senderRecipientId: RecipientId) {
|
||||
log(envelope.timestamp!!, "handleCallBusyMessage")
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(busy.id))
|
||||
val busyId = if (busy.id != null) {
|
||||
busy.id!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid busy, missing call id")
|
||||
return
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(busyId))
|
||||
ApplicationDependencies.getSignalCallManager().receivedCallBusy(CallMetadata(remotePeer, metadata.sourceDeviceId))
|
||||
}
|
||||
|
||||
private fun handleCallOpaqueMessage(envelope: Envelope, metadata: EnvelopeMetadata, opaque: Opaque, senderServiceId: ServiceId, serverDeliveredTimestamp: Long) {
|
||||
log(envelope.timestamp.toString(), "handleCallOpaqueMessage")
|
||||
log(envelope.timestamp!!, "handleCallOpaqueMessage")
|
||||
|
||||
val data = if (opaque.data_ != null) {
|
||||
opaque.data_!!.toByteArray()
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid opaque message, null data")
|
||||
return
|
||||
}
|
||||
|
||||
var messageAgeSeconds: Long = 0
|
||||
if (envelope.serverTimestamp in 1..serverDeliveredTimestamp) {
|
||||
messageAgeSeconds = (serverDeliveredTimestamp - envelope.serverTimestamp) / 1000
|
||||
messageAgeSeconds = (serverDeliveredTimestamp - envelope.serverTimestamp!!).milliseconds.inWholeSeconds
|
||||
}
|
||||
|
||||
ApplicationDependencies.getSignalCallManager()
|
||||
.receivedOpaqueMessage(
|
||||
OpaqueMessageMetadata(
|
||||
senderServiceId.rawUuid,
|
||||
opaque.data.toByteArray(),
|
||||
data,
|
||||
metadata.sourceDeviceId,
|
||||
messageAgeSeconds
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun Offer.toCallOfferMetadata(): OfferMetadata {
|
||||
val sdp = if (hasSdp()) sdp else null
|
||||
val opaque = if (hasOpaque()) opaque else null
|
||||
return OfferMetadata(opaque?.toByteArray(), sdp, OfferMessage.Type.fromProto(type))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import ProtoUtil.isNotEmpty
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import com.google.protobuf.ByteString
|
||||
import com.mobilecoin.lib.exceptions.SerializationException
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.Hex
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.signal.core.util.logging.Log
|
||||
@@ -36,6 +37,7 @@ import org.thoughtcrime.securesms.database.model.ParentStoryId
|
||||
import org.thoughtcrime.securesms.database.model.ParentStoryId.DirectReply
|
||||
import org.thoughtcrime.securesms.database.model.ParentStoryId.GroupReply
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
|
||||
import org.thoughtcrime.securesms.database.model.toBodyRangeList
|
||||
@@ -59,6 +61,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.debug
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.warn
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.expireTimerDuration
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.groupMasterKey
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.hasGroupContext
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.hasRemoteDelete
|
||||
@@ -98,12 +101,12 @@ import org.whispersystems.signalservice.api.payments.Money
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.util.OptionalUtil.asOptional
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.BodyRange
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Preview
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.Preview
|
||||
import java.security.SecureRandom
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
@@ -125,13 +128,13 @@ object DataMessageProcessor {
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?,
|
||||
localMetrics: SignalLocalMetrics.MessageReceive?
|
||||
) {
|
||||
val message: DataMessage = content.dataMessage
|
||||
val groupSecretParams = if (message.hasGroupContext) GroupSecretParams.deriveFromMasterKey(message.groupV2.groupMasterKey) else null
|
||||
val message: DataMessage = content.dataMessage!!
|
||||
val groupSecretParams = if (message.hasGroupContext) GroupSecretParams.deriveFromMasterKey(message.groupV2!!.groupMasterKey) else null
|
||||
val groupId: GroupId.V2? = if (groupSecretParams != null) GroupId.v2(groupSecretParams.publicParams.groupIdentifier) else null
|
||||
|
||||
var groupProcessResult: MessageContentProcessor.Gv2PreProcessResult? = null
|
||||
if (groupId != null) {
|
||||
groupProcessResult = MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp, content, metadata, groupId, message.groupV2, senderRecipient, groupSecretParams)
|
||||
groupProcessResult = MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp!!, content, metadata, groupId, message.groupV2!!, senderRecipient, groupSecretParams)
|
||||
if (groupProcessResult == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
|
||||
return
|
||||
}
|
||||
@@ -140,20 +143,20 @@ object DataMessageProcessor {
|
||||
|
||||
var messageId: MessageId? = null
|
||||
when {
|
||||
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, metadata.sourceDeviceId, groupId, envelope.timestamp)
|
||||
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, metadata.sourceDeviceId, groupId, envelope.timestamp!!)
|
||||
message.isEndSession -> messageId = handleEndSessionMessage(context, senderRecipient.id, envelope, metadata)
|
||||
message.isExpirationUpdate -> messageId = handleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient.id, groupId, message.expireTimer.seconds, receivedTime, false)
|
||||
message.isExpirationUpdate -> messageId = handleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient.id, groupId, message.expireTimerDuration, receivedTime, false)
|
||||
message.isStoryReaction -> messageId = handleStoryReaction(context, envelope, metadata, message, senderRecipient.id, groupId)
|
||||
message.hasReaction() -> messageId = handleReaction(context, envelope, message, senderRecipient.id, earlyMessageCacheEntry)
|
||||
message.reaction != null -> messageId = handleReaction(context, envelope, message, senderRecipient.id, earlyMessageCacheEntry)
|
||||
message.hasRemoteDelete -> messageId = handleRemoteDelete(context, envelope, message, senderRecipient.id, earlyMessageCacheEntry)
|
||||
message.isPaymentActivationRequest -> messageId = handlePaymentActivation(envelope, metadata, message, senderRecipient.id, receivedTime, isActivatePaymentsRequest = true, isPaymentsActivated = false)
|
||||
message.isPaymentActivated -> messageId = handlePaymentActivation(envelope, metadata, message, senderRecipient.id, receivedTime, isActivatePaymentsRequest = false, isPaymentsActivated = true)
|
||||
message.hasPayment() -> messageId = handlePayment(context, envelope, metadata, message, senderRecipient.id, receivedTime)
|
||||
message.hasStoryContext() -> messageId = handleStoryReply(context, envelope, metadata, message, senderRecipient, groupId, receivedTime)
|
||||
message.hasGiftBadge() -> messageId = handleGiftMessage(context, envelope, metadata, message, senderRecipient, threadRecipient.id, receivedTime)
|
||||
message.payment != null -> messageId = handlePayment(context, envelope, metadata, message, senderRecipient.id, receivedTime)
|
||||
message.storyContext != null -> messageId = handleStoryReply(context, envelope, metadata, message, senderRecipient, groupId, receivedTime)
|
||||
message.giftBadge != null -> messageId = handleGiftMessage(context, envelope, metadata, message, senderRecipient, threadRecipient.id, receivedTime)
|
||||
message.isMediaMessage -> messageId = handleMediaMessage(context, envelope, metadata, message, senderRecipient, threadRecipient, groupId, receivedTime, localMetrics)
|
||||
message.hasBody() -> messageId = handleTextMessage(context, envelope, metadata, message, senderRecipient, threadRecipient, groupId, receivedTime, localMetrics)
|
||||
message.hasGroupCallUpdate() -> handleGroupCallUpdateMessage(envelope, message, senderRecipient.id, groupId)
|
||||
message.body != null -> messageId = handleTextMessage(context, envelope, metadata, message, senderRecipient, threadRecipient, groupId, receivedTime, localMetrics)
|
||||
message.groupCallUpdate != null -> handleGroupCallUpdateMessage(envelope, message, senderRecipient.id, groupId)
|
||||
}
|
||||
|
||||
if (groupId != null) {
|
||||
@@ -162,12 +165,12 @@ object DataMessageProcessor {
|
||||
else -> SignalDatabase.groups.isUnknownGroup(groupId)
|
||||
}
|
||||
if (unknownGroup) {
|
||||
handleUnknownGroupMessage(envelope.timestamp, message.groupV2)
|
||||
handleUnknownGroupMessage(envelope.timestamp!!, message.groupV2!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (message.hasProfileKey()) {
|
||||
handleProfileKey(envelope.timestamp, message.profileKey.toByteArray(), senderRecipient)
|
||||
if (message.profileKey.isNotEmpty()) {
|
||||
handleProfileKey(envelope.timestamp!!, message.profileKey!!.toByteArray(), senderRecipient)
|
||||
}
|
||||
|
||||
if (groupId == null && senderRecipient.hiddenState == HiddenState.HIDDEN) {
|
||||
@@ -175,7 +178,7 @@ object DataMessageProcessor {
|
||||
}
|
||||
|
||||
if (metadata.sealedSender && messageId != null) {
|
||||
SignalExecutors.BOUNDED.execute { ApplicationDependencies.getJobManager().add(SendDeliveryReceiptJob(senderRecipient.id, message.timestamp, messageId)) }
|
||||
SignalExecutors.BOUNDED.execute { ApplicationDependencies.getJobManager().add(SendDeliveryReceiptJob(senderRecipient.id, message.timestamp!!, messageId)) }
|
||||
} else if (!metadata.sealedSender) {
|
||||
if (RecipientUtil.shouldHaveProfileKey(threadRecipient)) {
|
||||
Log.w(MessageContentProcessor.TAG, "Received an unsealed sender message from " + senderRecipient.id + ", but they should already have our profile key. Correcting.")
|
||||
@@ -260,13 +263,13 @@ object DataMessageProcessor {
|
||||
envelope: Envelope,
|
||||
metadata: EnvelopeMetadata
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "End session message.")
|
||||
log(envelope.timestamp!!, "End session message.")
|
||||
|
||||
val incomingTextMessage = IncomingTextMessage(
|
||||
senderRecipientId,
|
||||
metadata.sourceDeviceId,
|
||||
envelope.timestamp,
|
||||
envelope.serverTimestamp,
|
||||
envelope.timestamp!!,
|
||||
envelope.serverTimestamp!!,
|
||||
System.currentTimeMillis(),
|
||||
"",
|
||||
Optional.empty(),
|
||||
@@ -302,23 +305,23 @@ object DataMessageProcessor {
|
||||
receivedTime: Long,
|
||||
sideEffect: Boolean
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Expiration update. Side effect: $sideEffect")
|
||||
log(envelope.timestamp!!, "Expiration update. Side effect: $sideEffect")
|
||||
|
||||
if (groupId != null) {
|
||||
warn(envelope.timestamp, "Expiration update received for GV2. Ignoring.")
|
||||
warn(envelope.timestamp!!, "Expiration update received for GV2. Ignoring.")
|
||||
return null
|
||||
}
|
||||
|
||||
if (SignalDatabase.recipients.getExpiresInSeconds(threadRecipientId) == expiresIn.inWholeSeconds) {
|
||||
log(envelope.timestamp, "No change in message expiry for group. Ignoring.")
|
||||
log(envelope.timestamp!!, "No change in message expiry for group. Ignoring.")
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipientId,
|
||||
sentTimeMillis = envelope.timestamp - if (sideEffect) 1 else 0,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!! - if (sideEffect) 1 else 0,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = receivedTime,
|
||||
expiresIn = expiresIn.inWholeMilliseconds,
|
||||
isExpirationUpdate = true,
|
||||
@@ -354,7 +357,7 @@ object DataMessageProcessor {
|
||||
receivedTime: Long
|
||||
) {
|
||||
if (threadRecipient.expiresInSeconds.toLong() != expiresIn.inWholeSeconds) {
|
||||
warn(envelope.timestamp, "Message expire time didn't match thread expire time. Handling timer update.")
|
||||
warn(envelope.timestamp!!, "Message expire time didn't match thread expire time. Handling timer update.")
|
||||
handleExpirationUpdate(envelope, metadata, senderRecipientId, threadRecipient.id, groupId, expiresIn, receivedTime, true)
|
||||
}
|
||||
}
|
||||
@@ -368,16 +371,18 @@ object DataMessageProcessor {
|
||||
senderRecipientId: RecipientId,
|
||||
groupId: GroupId.V2?
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Story reaction.")
|
||||
log(envelope.timestamp!!, "Story reaction.")
|
||||
|
||||
val storyContext = message.storyContext!!
|
||||
val emoji = message.reaction!!.emoji
|
||||
|
||||
val emoji = message.reaction.emoji
|
||||
if (!EmojiUtil.isEmoji(emoji)) {
|
||||
warn(envelope.timestamp, "Story reaction text is not a valid emoji! Ignoring the message.")
|
||||
warn(envelope.timestamp!!, "Story reaction text is not a valid emoji! Ignoring the message.")
|
||||
return null
|
||||
}
|
||||
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(message.storyContext.authorAci)
|
||||
val sentTimestamp = message.storyContext.sentTimestamp
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(storyContext.authorAci!!)
|
||||
val sentTimestamp = storyContext.sentTimestamp!!
|
||||
|
||||
SignalDatabase.messages.beginTransaction()
|
||||
return try {
|
||||
@@ -403,20 +408,20 @@ object DataMessageProcessor {
|
||||
|
||||
parentStoryId = DirectReply(storyId)
|
||||
quoteModel = QuoteModel(sentTimestamp, authorRecipientId, displayText, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
|
||||
expiresIn = message.expireTimer.seconds
|
||||
expiresIn = message.expireTimerDuration
|
||||
} else {
|
||||
warn(envelope.timestamp, "Story has reactions disabled. Dropping reaction.")
|
||||
warn(envelope.timestamp!!, "Story has reactions disabled. Dropping reaction.")
|
||||
return null
|
||||
}
|
||||
} catch (e: NoSuchMessageException) {
|
||||
warn(envelope.timestamp, "Couldn't find story for reaction.", e)
|
||||
warn(envelope.timestamp!!, "Couldn't find story for reaction.", e)
|
||||
return null
|
||||
}
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipientId,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = System.currentTimeMillis(),
|
||||
parentStoryId = parentStoryId,
|
||||
isStoryReaction = true,
|
||||
@@ -445,7 +450,7 @@ object DataMessageProcessor {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
warn(envelope.timestamp, "Failed to insert story reaction")
|
||||
warn(envelope.timestamp!!, "Failed to insert story reaction")
|
||||
null
|
||||
}
|
||||
} catch (e: MmsException) {
|
||||
@@ -463,27 +468,29 @@ object DataMessageProcessor {
|
||||
senderRecipientId: RecipientId,
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Handle reaction for message " + message.reaction.targetSentTimestamp)
|
||||
val reaction: DataMessage.Reaction = message.reaction!!
|
||||
|
||||
val emoji: String = message.reaction.emoji
|
||||
val isRemove: Boolean = message.reaction.remove
|
||||
val targetAuthorServiceId: ServiceId = ServiceId.parseOrThrow(message.reaction.targetAuthorAci)
|
||||
val targetSentTimestamp = message.reaction.targetSentTimestamp
|
||||
log(envelope.timestamp!!, "Handle reaction for message " + reaction.targetSentTimestamp!!)
|
||||
|
||||
val emoji: String? = reaction.emoji
|
||||
val isRemove: Boolean = reaction.remove ?: false
|
||||
val targetAuthorServiceId: ServiceId = ServiceId.parseOrThrow(reaction.targetAuthorAci!!)
|
||||
val targetSentTimestamp: Long = reaction.targetSentTimestamp!!
|
||||
|
||||
if (targetAuthorServiceId.isUnknown) {
|
||||
warn(envelope.timestamp, "Reaction was to an unknown UUID! Ignoring the message.")
|
||||
warn(envelope.timestamp!!, "Reaction was to an unknown UUID! Ignoring the message.")
|
||||
return null
|
||||
}
|
||||
|
||||
if (!EmojiUtil.isEmoji(emoji)) {
|
||||
warn(envelope.timestamp, "Reaction text is not a valid emoji! Ignoring the message.")
|
||||
warn(envelope.timestamp!!, "Reaction text is not a valid emoji! Ignoring the message.")
|
||||
return null
|
||||
}
|
||||
|
||||
val targetAuthor = Recipient.externalPush(targetAuthorServiceId)
|
||||
val targetMessage = SignalDatabase.messages.getMessageFor(targetSentTimestamp, targetAuthor.id)
|
||||
if (targetMessage == null) {
|
||||
warn(envelope.timestamp, "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
warn(envelope.timestamp!!, "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(targetAuthor.id, targetSentTimestamp, earlyMessageCacheEntry)
|
||||
PushProcessEarlyMessagesJob.enqueue()
|
||||
@@ -492,25 +499,25 @@ object DataMessageProcessor {
|
||||
}
|
||||
|
||||
if (targetMessage.isRemoteDelete) {
|
||||
warn(envelope.timestamp, "[handleReaction] Found a matching message, but it's flagged as remotely deleted. timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
warn(envelope.timestamp!!, "[handleReaction] Found a matching message, but it's flagged as remotely deleted. timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
return null
|
||||
}
|
||||
|
||||
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
|
||||
if (targetThread == null) {
|
||||
warn(envelope.timestamp, "[handleReaction] Could not find a thread for the message! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
warn(envelope.timestamp!!, "[handleReaction] Could not find a thread for the message! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
return null
|
||||
}
|
||||
|
||||
val targetThreadRecipientId = targetThread.recipient.id
|
||||
val groupRecord = SignalDatabase.groups.getGroup(targetThreadRecipientId).orNull()
|
||||
if (groupRecord != null && !groupRecord.members.contains(senderRecipientId)) {
|
||||
warn(envelope.timestamp, "[handleReaction] Reaction author is not in the group! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
warn(envelope.timestamp!!, "[handleReaction] Reaction author is not in the group! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
return null
|
||||
}
|
||||
|
||||
if (groupRecord == null && senderRecipientId != targetThreadRecipientId && Recipient.self().id != senderRecipientId) {
|
||||
warn(envelope.timestamp, "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
warn(envelope.timestamp!!, "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -520,7 +527,7 @@ object DataMessageProcessor {
|
||||
SignalDatabase.reactions.deleteReaction(targetMessageId, senderRecipientId)
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context)
|
||||
} else {
|
||||
val reactionRecord = ReactionRecord(emoji, senderRecipientId, message.timestamp, System.currentTimeMillis())
|
||||
val reactionRecord = ReactionRecord(emoji!!, senderRecipientId, message.timestamp!!, System.currentTimeMillis())
|
||||
SignalDatabase.reactions.addReaction(targetMessageId, reactionRecord)
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.fromMessageRecord(targetMessage), false)
|
||||
}
|
||||
@@ -529,12 +536,14 @@ object DataMessageProcessor {
|
||||
}
|
||||
|
||||
fun handleRemoteDelete(context: Context, envelope: Envelope, message: DataMessage, senderRecipientId: RecipientId, earlyMessageCacheEntry: EarlyMessageCacheEntry?): MessageId? {
|
||||
log(envelope.timestamp, "Remote delete for message ${message.delete.targetSentTimestamp}")
|
||||
val delete = message.delete!!
|
||||
|
||||
val targetSentTimestamp: Long = message.delete.targetSentTimestamp
|
||||
log(envelope.timestamp!!, "Remote delete for message ${delete.targetSentTimestamp}")
|
||||
|
||||
val targetSentTimestamp: Long = delete.targetSentTimestamp!!
|
||||
val targetMessage: MessageRecord? = SignalDatabase.messages.getMessageFor(targetSentTimestamp, senderRecipientId)
|
||||
|
||||
return if (targetMessage != null && MessageConstraintsUtil.isValidRemoteDeleteReceive(targetMessage, senderRecipientId, envelope.serverTimestamp)) {
|
||||
return if (targetMessage != null && MessageConstraintsUtil.isValidRemoteDeleteReceive(targetMessage, senderRecipientId, envelope.serverTimestamp!!)) {
|
||||
SignalDatabase.messages.markAsRemoteDelete(targetMessage)
|
||||
if (targetMessage.isStory()) {
|
||||
SignalDatabase.messages.deleteRemotelyDeletedStory(targetMessage.id)
|
||||
@@ -544,7 +553,7 @@ object DataMessageProcessor {
|
||||
|
||||
MessageId(targetMessage.id)
|
||||
} else if (targetMessage == null) {
|
||||
warn(envelope.timestamp, "[handleRemoteDelete] Could not find matching message! timestamp: $targetSentTimestamp author: $senderRecipientId")
|
||||
warn(envelope.timestamp!!, "[handleRemoteDelete] Could not find matching message! timestamp: $targetSentTimestamp author: $senderRecipientId")
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(senderRecipientId, targetSentTimestamp, earlyMessageCacheEntry)
|
||||
PushProcessEarlyMessagesJob.enqueue()
|
||||
@@ -552,7 +561,7 @@ object DataMessageProcessor {
|
||||
|
||||
null
|
||||
} else {
|
||||
warn(envelope.timestamp, "[handleRemoteDelete] Invalid remote delete! deleteTime: ${envelope.serverTimestamp}, targetTime: ${targetMessage.serverTimestamp}, deleteAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
|
||||
warn(envelope.timestamp!!, "[handleRemoteDelete] Invalid remote delete! deleteTime: ${envelope.serverTimestamp!!}, targetTime: ${targetMessage.serverTimestamp}, deleteAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -572,14 +581,14 @@ object DataMessageProcessor {
|
||||
isActivatePaymentsRequest: Boolean,
|
||||
isPaymentsActivated: Boolean
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Payment activation request: $isActivatePaymentsRequest activated: $isPaymentsActivated")
|
||||
log(envelope.timestamp!!, "Payment activation request: $isActivatePaymentsRequest activated: $isPaymentsActivated")
|
||||
try {
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipientId,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = receivedTime,
|
||||
expiresIn = message.expireTimer.seconds.inWholeMilliseconds,
|
||||
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
serverGuid = envelope.serverGuid,
|
||||
isActivatePaymentsRequest = isActivatePaymentsRequest,
|
||||
@@ -606,14 +615,14 @@ object DataMessageProcessor {
|
||||
senderRecipientId: RecipientId,
|
||||
receivedTime: Long
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Payment message.")
|
||||
log(envelope.timestamp!!, "Payment message.")
|
||||
|
||||
if (!message.payment.notification.mobileCoin.hasReceipt()) {
|
||||
warn(envelope.timestamp, "Ignoring payment message without notification")
|
||||
if (message.payment?.notification?.mobileCoin?.receipt == null) {
|
||||
warn(envelope.timestamp!!, "Ignoring payment message without notification")
|
||||
return null
|
||||
}
|
||||
|
||||
val paymentNotification = message.payment.notification
|
||||
val paymentNotification = message.payment!!.notification!!
|
||||
val uuid = UUID.randomUUID()
|
||||
val queue = "Payment_" + PushProcessMessageJob.getQueueName(senderRecipientId)
|
||||
|
||||
@@ -621,21 +630,21 @@ object DataMessageProcessor {
|
||||
SignalDatabase.payments.createIncomingPayment(
|
||||
uuid,
|
||||
senderRecipientId,
|
||||
message.timestamp,
|
||||
paymentNotification.note,
|
||||
message.timestamp!!,
|
||||
paymentNotification.note ?: "",
|
||||
Money.MobileCoin.ZERO,
|
||||
Money.MobileCoin.ZERO,
|
||||
paymentNotification.mobileCoin.receipt.toByteArray(),
|
||||
paymentNotification.mobileCoin!!.receipt!!.toByteArray(),
|
||||
true
|
||||
)
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipientId,
|
||||
body = uuid.toString(),
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = receivedTime,
|
||||
expiresIn = message.expireTimer.seconds.inWholeMilliseconds,
|
||||
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
serverGuid = envelope.serverGuid,
|
||||
isPushMessage = true,
|
||||
@@ -649,9 +658,9 @@ object DataMessageProcessor {
|
||||
return messageId
|
||||
}
|
||||
} catch (e: PublicKeyConflictException) {
|
||||
warn(envelope.timestamp, "Ignoring payment with public key already in database")
|
||||
warn(envelope.timestamp!!, "Ignoring payment with public key already in database")
|
||||
} catch (e: SerializationException) {
|
||||
warn(envelope.timestamp, "Ignoring payment with bad data.", e)
|
||||
warn(envelope.timestamp!!, "Ignoring payment with bad data.", e)
|
||||
} catch (e: MmsException) {
|
||||
throw StorageFailedException(e, metadata.sourceServiceId.toString(), metadata.sourceDeviceId)
|
||||
} finally {
|
||||
@@ -676,10 +685,16 @@ object DataMessageProcessor {
|
||||
groupId: GroupId.V2?,
|
||||
receivedTime: Long
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Story reply.")
|
||||
log(envelope.timestamp!!, "Story reply.")
|
||||
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(message.storyContext.authorAci)
|
||||
val sentTimestamp = message.storyContext.sentTimestamp
|
||||
val storyContext: DataMessage.StoryContext = message.storyContext!!
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(storyContext.authorAci!!)
|
||||
val sentTimestamp: Long = if (storyContext.sentTimestamp != null) {
|
||||
storyContext.sentTimestamp!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Invalid story reply, missing sentTimestamp")
|
||||
return null
|
||||
}
|
||||
|
||||
SignalDatabase.messages.beginTransaction()
|
||||
return try {
|
||||
@@ -708,7 +723,7 @@ object DataMessageProcessor {
|
||||
threadRecipient = senderRecipient
|
||||
}
|
||||
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimer.seconds, receivedTime)
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimerDuration, receivedTime)
|
||||
|
||||
if (message.hasGroupContext) {
|
||||
parentStoryId = GroupReply(storyMessageId.id)
|
||||
@@ -723,22 +738,22 @@ object DataMessageProcessor {
|
||||
}
|
||||
|
||||
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipientId, displayText, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
|
||||
expiresInMillis = message.expireTimer.seconds
|
||||
expiresInMillis = message.expireTimerDuration
|
||||
} else {
|
||||
warn(envelope.timestamp, "Story has replies disabled. Dropping reply.")
|
||||
warn(envelope.timestamp!!, "Story has replies disabled. Dropping reply.")
|
||||
return null
|
||||
}
|
||||
} catch (e: NoSuchMessageException) {
|
||||
warn(envelope.timestamp, "Couldn't find story for reply.", e)
|
||||
warn(envelope.timestamp!!, "Couldn't find story for reply.", e)
|
||||
return null
|
||||
}
|
||||
|
||||
val bodyRanges: BodyRangeList? = message.bodyRangesList.filter { it.hasStyle() }.toList().toBodyRangeList()
|
||||
val bodyRanges: BodyRangeList? = message.bodyRanges.filter { it.mentionAci == null }.toList().toBodyRangeList()
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipient.id,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = System.currentTimeMillis(),
|
||||
parentStoryId = parentStoryId,
|
||||
expiresIn = expiresInMillis.inWholeMilliseconds,
|
||||
@@ -746,7 +761,7 @@ object DataMessageProcessor {
|
||||
body = message.body,
|
||||
groupId = groupId,
|
||||
quote = quoteModel,
|
||||
mentions = getMentions(message.bodyRangesList),
|
||||
mentions = getMentions(message.bodyRanges),
|
||||
serverGuid = envelope.serverGuid,
|
||||
messageRanges = bodyRanges
|
||||
)
|
||||
@@ -769,7 +784,7 @@ object DataMessageProcessor {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
warn(envelope.timestamp, "Failed to insert story reply.")
|
||||
warn(envelope.timestamp!!, "Failed to insert story reply.")
|
||||
null
|
||||
}
|
||||
} catch (e: MmsException) {
|
||||
@@ -789,29 +804,30 @@ object DataMessageProcessor {
|
||||
threadRecipientId: RecipientId,
|
||||
receivedTime: Long
|
||||
): MessageId? {
|
||||
log(message.timestamp, "Gift message.")
|
||||
log(message.timestamp!!, "Gift message.")
|
||||
|
||||
check(message.giftBadge.hasReceiptCredentialPresentation())
|
||||
val giftBadge: DataMessage.GiftBadge = message.giftBadge!!
|
||||
check(giftBadge.receiptCredentialPresentation != null)
|
||||
|
||||
notifyTypingStoppedFromIncomingMessage(context, senderRecipient, threadRecipientId, metadata.sourceDeviceId)
|
||||
|
||||
val token = ReceiptCredentialPresentation(message.giftBadge.receiptCredentialPresentation.toByteArray()).serialize()
|
||||
val giftBadge = GiftBadge.newBuilder()
|
||||
.setRedemptionToken(ByteString.copyFrom(token))
|
||||
.setRedemptionState(GiftBadge.RedemptionState.PENDING)
|
||||
val token = ReceiptCredentialPresentation(giftBadge.receiptCredentialPresentation!!.toByteArray()).serialize()
|
||||
val dbGiftBadge = GiftBadge.Builder()
|
||||
.redemptionToken(token.toByteString())
|
||||
.redemptionState(GiftBadge.RedemptionState.PENDING)
|
||||
.build()
|
||||
|
||||
val insertResult: InsertResult? = try {
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipient.id,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = receivedTime,
|
||||
expiresIn = message.expireTimer.seconds.inWholeMilliseconds,
|
||||
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
body = Base64.encodeBytes(giftBadge.toByteArray()),
|
||||
body = Base64.encodeBytes(dbGiftBadge.encode()),
|
||||
serverGuid = envelope.serverGuid,
|
||||
giftBadge = giftBadge
|
||||
giftBadge = dbGiftBadge
|
||||
)
|
||||
|
||||
SignalDatabase.messages.insertSecureDecryptedMessageInbox(mediaMessage, -1).orNull()
|
||||
@@ -840,7 +856,7 @@ object DataMessageProcessor {
|
||||
receivedTime: Long,
|
||||
localMetrics: SignalLocalMetrics.MessageReceive?
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Media message.")
|
||||
log(envelope.timestamp!!, "Media message.")
|
||||
|
||||
notifyTypingStoppedFromIncomingMessage(context, senderRecipient, threadRecipient.id, metadata.sourceDeviceId)
|
||||
|
||||
@@ -848,25 +864,25 @@ object DataMessageProcessor {
|
||||
|
||||
SignalDatabase.messages.beginTransaction()
|
||||
try {
|
||||
val quote: QuoteModel? = getValidatedQuote(context, envelope.timestamp, message)
|
||||
val quote: QuoteModel? = getValidatedQuote(context, envelope.timestamp!!, message)
|
||||
val contacts: List<Contact> = getContacts(message)
|
||||
val linkPreviews: List<LinkPreview> = getLinkPreviews(message.previewList, message.body ?: "", false)
|
||||
val mentions: List<Mention> = getMentions(message.bodyRangesList.take(BODY_RANGE_PROCESSING_LIMIT))
|
||||
val sticker: Attachment? = getStickerAttachment(envelope.timestamp, message)
|
||||
val attachments: List<Attachment> = message.attachmentsList.toPointersWithinLimit()
|
||||
val messageRanges: BodyRangeList? = if (message.bodyRangesCount > 0) message.bodyRangesList.asSequence().take(BODY_RANGE_PROCESSING_LIMIT).filter { it.hasStyle() }.toList().toBodyRangeList() else null
|
||||
val linkPreviews: List<LinkPreview> = getLinkPreviews(message.preview, message.body ?: "", false)
|
||||
val mentions: List<Mention> = getMentions(message.bodyRanges.take(BODY_RANGE_PROCESSING_LIMIT))
|
||||
val sticker: Attachment? = getStickerAttachment(envelope.timestamp!!, message)
|
||||
val attachments: List<Attachment> = message.attachments.toPointersWithinLimit()
|
||||
val messageRanges: BodyRangeList? = if (message.bodyRanges.isNotEmpty()) message.bodyRanges.asSequence().take(BODY_RANGE_PROCESSING_LIMIT).filter { it.mentionAci == null }.toList().toBodyRangeList() else null
|
||||
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimer.seconds, receivedTime)
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimerDuration, receivedTime)
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipient.id,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = receivedTime,
|
||||
expiresIn = message.expireTimer.seconds.inWholeMilliseconds,
|
||||
isViewOnce = message.isViewOnce,
|
||||
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
|
||||
isViewOnce = message.isViewOnce == true,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
body = message.body.ifEmpty { null },
|
||||
body = message.body?.ifEmpty { null },
|
||||
groupId = groupId,
|
||||
attachments = attachments + if (sticker != null) listOf(sticker) else emptyList(),
|
||||
quote = quote,
|
||||
@@ -909,7 +925,7 @@ object DataMessageProcessor {
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.threadId))
|
||||
TrimThreadJob.enqueueAsync(insertResult.threadId)
|
||||
|
||||
if (message.isViewOnce) {
|
||||
if (message.isViewOnce == true) {
|
||||
ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary()
|
||||
}
|
||||
}
|
||||
@@ -932,23 +948,23 @@ object DataMessageProcessor {
|
||||
receivedTime: Long,
|
||||
localMetrics: SignalLocalMetrics.MessageReceive?
|
||||
): MessageId? {
|
||||
log(envelope.timestamp, "Text message.")
|
||||
log(envelope.timestamp!!, "Text message.")
|
||||
|
||||
val body = if (message.hasBody()) message.body else ""
|
||||
val body = message.body ?: ""
|
||||
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimer.seconds, receivedTime)
|
||||
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimerDuration, receivedTime)
|
||||
|
||||
notifyTypingStoppedFromIncomingMessage(context, senderRecipient, threadRecipient.id, metadata.sourceDeviceId)
|
||||
|
||||
val textMessage = IncomingTextMessage(
|
||||
senderRecipient.id,
|
||||
metadata.sourceDeviceId,
|
||||
envelope.timestamp,
|
||||
envelope.serverTimestamp,
|
||||
envelope.timestamp!!,
|
||||
envelope.serverTimestamp!!,
|
||||
receivedTime,
|
||||
body,
|
||||
Optional.ofNullable(groupId),
|
||||
message.expireTimer.seconds.inWholeMilliseconds,
|
||||
message.expireTimerDuration.inWholeMilliseconds,
|
||||
metadata.sealedSender,
|
||||
envelope.serverGuid
|
||||
)
|
||||
@@ -970,10 +986,12 @@ object DataMessageProcessor {
|
||||
senderRecipientId: RecipientId,
|
||||
groupId: GroupId.V2?
|
||||
) {
|
||||
log(envelope.timestamp, "Group call update message.")
|
||||
log(envelope.timestamp!!, "Group call update message.")
|
||||
|
||||
if (groupId == null || !message.hasGroupCallUpdate()) {
|
||||
warn(envelope.timestamp, "Invalid group for group call update message")
|
||||
val groupCallUpdate: DataMessage.GroupCallUpdate = message.groupCallUpdate!!
|
||||
|
||||
if (groupId == null) {
|
||||
warn(envelope.timestamp!!, "Invalid group for group call update message")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -982,8 +1000,8 @@ object DataMessageProcessor {
|
||||
SignalDatabase.calls.insertOrUpdateGroupCallFromExternalEvent(
|
||||
groupRecipientId,
|
||||
senderRecipientId,
|
||||
envelope.serverTimestamp,
|
||||
if (message.groupCallUpdate.hasEraId()) message.groupCallUpdate.eraId else null
|
||||
envelope.serverTimestamp!!,
|
||||
groupCallUpdate.eraId
|
||||
)
|
||||
|
||||
GroupCallPeekJob.enqueue(groupRecipientId)
|
||||
@@ -1000,13 +1018,13 @@ object DataMessageProcessor {
|
||||
|
||||
fun getMentions(mentionBodyRanges: List<BodyRange>): List<Mention> {
|
||||
return mentionBodyRanges
|
||||
.filter { it.hasMentionAci() }
|
||||
.filter { it.mentionAci != null && it.start != null && it.length != null }
|
||||
.mapNotNull {
|
||||
val aci = ACI.parseOrNull(it.mentionAci)
|
||||
|
||||
if (aci != null && !aci.isUnknown) {
|
||||
val id = Recipient.externalPush(aci).id
|
||||
Mention(id, it.start, it.length)
|
||||
Mention(id, it.start!!, it.length!!)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -1030,19 +1048,15 @@ object DataMessageProcessor {
|
||||
}
|
||||
|
||||
fun getValidatedQuote(context: Context, timestamp: Long, message: DataMessage): QuoteModel? {
|
||||
if (!message.hasQuote()) {
|
||||
return null
|
||||
}
|
||||
val quote: DataMessage.Quote = message.quote ?: return null
|
||||
|
||||
val quote: DataMessage.Quote = message.quote
|
||||
|
||||
if (quote.id <= 0) {
|
||||
if (quote.id == null) {
|
||||
warn(timestamp, "Received quote without an ID! Ignoring...")
|
||||
return null
|
||||
}
|
||||
|
||||
val authorId = Recipient.externalPush(ServiceId.parseOrThrow(quote.authorAci)).id
|
||||
var quotedMessage = SignalDatabase.messages.getMessageFor(quote.id, authorId) as? MediaMmsMessageRecord
|
||||
val authorId = Recipient.externalPush(ServiceId.parseOrThrow(quote.authorAci!!)).id
|
||||
var quotedMessage = SignalDatabase.messages.getMessageFor(quote.id!!, authorId) as? MediaMmsMessageRecord
|
||||
|
||||
if (quotedMessage != null && !quotedMessage.isRemoteDelete) {
|
||||
log(timestamp, "Found matching message record...")
|
||||
@@ -1074,7 +1088,7 @@ object DataMessageProcessor {
|
||||
val body = if (quotedMessage.isPaymentNotification) quotedMessage.getDisplayBody(context).toString() else quotedMessage.body
|
||||
|
||||
return QuoteModel(
|
||||
quote.id,
|
||||
quote.id!!,
|
||||
authorId,
|
||||
body,
|
||||
false,
|
||||
@@ -1089,19 +1103,19 @@ object DataMessageProcessor {
|
||||
|
||||
warn(timestamp, "Didn't find matching message record...")
|
||||
return QuoteModel(
|
||||
quote.id,
|
||||
quote.id!!,
|
||||
authorId,
|
||||
quote.text,
|
||||
quote.text ?: "",
|
||||
true,
|
||||
quote.attachmentsList.mapNotNull { PointerAttachment.forPointer(it).orNull() },
|
||||
getMentions(quote.bodyRangesList),
|
||||
quote.attachments.mapNotNull { PointerAttachment.forPointer(it).orNull() },
|
||||
getMentions(quote.bodyRanges),
|
||||
QuoteModel.Type.fromProto(quote.type),
|
||||
quote.bodyRangesList.filterNot { it.hasMentionAci() }.toBodyRangeList()
|
||||
quote.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
|
||||
)
|
||||
}
|
||||
|
||||
fun getContacts(message: DataMessage): List<Contact> {
|
||||
return message.contactList.map { ContactModelMapper.remoteToLocal(it) }
|
||||
return message.contact.map { ContactModelMapper.remoteToLocal(it) }
|
||||
}
|
||||
|
||||
fun getLinkPreviews(previews: List<Preview>, body: String, isStoryEmbed: Boolean): List<LinkPreview> {
|
||||
@@ -1113,7 +1127,7 @@ object DataMessageProcessor {
|
||||
|
||||
return previews
|
||||
.mapNotNull { preview ->
|
||||
val thumbnail: Attachment? = preview.image.toPointer()
|
||||
val thumbnail: Attachment? = preview.image?.toPointer()
|
||||
val url: Optional<String> = preview.url.toOptional()
|
||||
val title: Optional<String> = preview.title.toOptional()
|
||||
val description: Optional<String> = preview.description.toOptional()
|
||||
@@ -1122,34 +1136,31 @@ object DataMessageProcessor {
|
||||
val validDomain = url.isPresent && LinkUtil.isValidPreviewUrl(url.get())
|
||||
val isForCallLink = url.isPresent && CallLinks.isCallLink(url.get())
|
||||
|
||||
if ((hasTitle || isForCallLink) && (presentInBody || isStoryEmbed) && validDomain) {
|
||||
val linkPreview = LinkPreview(url.get(), title.orElse(""), description.orElse(""), preview.date, thumbnail.toOptional())
|
||||
if ((hasTitle || isForCallLink) && (presentInBody || isStoryEmbed) && validDomain && preview.date != null) {
|
||||
val linkPreview = LinkPreview(url.get(), title.orElse(""), description.orElse(""), preview.date!!, thumbnail.toOptional())
|
||||
linkPreview
|
||||
} else {
|
||||
warn(String.format("Discarding an invalid link preview. hasTitle: %b presentInBody: %b isStoryEmbed: %b validDomain: %b", hasTitle, presentInBody, isStoryEmbed, validDomain))
|
||||
warn(String.format("Discarding an invalid link preview. hasTitle: %b presentInBody: %b isStoryEmbed: %b validDomain: %b date: %b", hasTitle, presentInBody, isStoryEmbed, validDomain, preview.date != null))
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getStickerAttachment(timestamp: Long, message: DataMessage): Attachment? {
|
||||
if (!message.hasSticker()) {
|
||||
return null
|
||||
}
|
||||
val sticker = message.sticker ?: return null
|
||||
|
||||
val sticker = message.sticker
|
||||
if (!(message.sticker.hasPackId() && message.sticker.hasPackKey() && message.sticker.hasStickerId() && message.sticker.hasData())) {
|
||||
if (sticker.packId == null || sticker.packKey == null || sticker.stickerId == null || sticker.data_ == null) {
|
||||
warn(timestamp, "Malformed sticker!")
|
||||
return null
|
||||
}
|
||||
|
||||
val packId = Hex.toStringCondensed(sticker.packId.toByteArray())
|
||||
val packKey = Hex.toStringCondensed(sticker.packKey.toByteArray())
|
||||
val stickerId = sticker.stickerId
|
||||
val emoji = sticker.emoji
|
||||
val stickerLocator = StickerLocator(packId, packKey, stickerId, emoji)
|
||||
val packId: String = Hex.toStringCondensed(sticker.packId!!.toByteArray())
|
||||
val packKey: String = Hex.toStringCondensed(sticker.packKey!!.toByteArray())
|
||||
val stickerId: Int = sticker.stickerId!!
|
||||
val emoji: String? = sticker.emoji
|
||||
|
||||
val stickerRecord = SignalDatabase.stickers.getSticker(stickerLocator.packId, stickerLocator.stickerId, false)
|
||||
val stickerLocator = StickerLocator(packId, packKey, stickerId, emoji)
|
||||
val stickerRecord: StickerRecord? = SignalDatabase.stickers.getSticker(stickerLocator.packId, stickerLocator.stickerId, false)
|
||||
|
||||
return if (stickerRecord != null) {
|
||||
UriAttachment(
|
||||
@@ -1172,7 +1183,7 @@ object DataMessageProcessor {
|
||||
null
|
||||
)
|
||||
} else {
|
||||
sticker.data.toPointer(stickerLocator)
|
||||
sticker.data_!!.toPointer(stickerLocator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ import org.thoughtcrime.securesms.util.MessageConstraintsUtil
|
||||
import org.thoughtcrime.securesms.util.hasAudio
|
||||
import org.thoughtcrime.securesms.util.hasSharedContact
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import java.util.Optional
|
||||
|
||||
object EditMessageProcessor {
|
||||
@@ -43,45 +43,45 @@ object EditMessageProcessor {
|
||||
senderRecipient: Recipient,
|
||||
threadRecipient: Recipient,
|
||||
envelope: Envelope,
|
||||
content: SignalServiceProtos.Content,
|
||||
content: Content,
|
||||
metadata: EnvelopeMetadata,
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
) {
|
||||
val editMessage = content.editMessage
|
||||
val editMessage = content.editMessage!!
|
||||
|
||||
log(envelope.timestamp, "[handleEditMessage] Edit message for " + editMessage.targetSentTimestamp)
|
||||
log(envelope.timestamp!!, "[handleEditMessage] Edit message for " + editMessage.targetSentTimestamp)
|
||||
|
||||
var targetMessage: MediaMmsMessageRecord? = SignalDatabase.messages.getMessageFor(editMessage.targetSentTimestamp, senderRecipient.id) as? MediaMmsMessageRecord
|
||||
var targetMessage: MediaMmsMessageRecord? = SignalDatabase.messages.getMessageFor(editMessage.targetSentTimestamp!!, senderRecipient.id) as? MediaMmsMessageRecord
|
||||
val targetThreadRecipient: Recipient? = if (targetMessage != null) SignalDatabase.threads.getRecipientForThreadId(targetMessage.threadId) else null
|
||||
|
||||
if (targetMessage == null || targetThreadRecipient == null) {
|
||||
warn(envelope.timestamp, "[handleEditMessage] Could not find matching message! timestamp: ${editMessage.targetSentTimestamp} author: ${senderRecipient.id}")
|
||||
warn(envelope.timestamp!!, "[handleEditMessage] Could not find matching message! timestamp: ${editMessage.targetSentTimestamp} author: ${senderRecipient.id}")
|
||||
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(senderRecipient.id, editMessage.targetSentTimestamp, earlyMessageCacheEntry)
|
||||
ApplicationDependencies.getEarlyMessageCache().store(senderRecipient.id, editMessage.targetSentTimestamp!!, earlyMessageCacheEntry)
|
||||
PushProcessEarlyMessagesJob.enqueue()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val message = editMessage.dataMessage
|
||||
val message = editMessage.dataMessage!!
|
||||
val isMediaMessage = message.isMediaMessage
|
||||
val groupId: GroupId.V2? = message.groupV2.groupId
|
||||
val groupId: GroupId.V2? = message.groupV2?.groupId
|
||||
|
||||
val originalMessage = targetMessage.originalMessageId?.let { SignalDatabase.messages.getMessageRecord(it.id) } ?: targetMessage
|
||||
val validTiming = MessageConstraintsUtil.isValidEditMessageReceive(originalMessage, senderRecipient, envelope.serverTimestamp)
|
||||
val validTiming = MessageConstraintsUtil.isValidEditMessageReceive(originalMessage, senderRecipient, envelope.serverTimestamp!!)
|
||||
val validAuthor = senderRecipient.id == originalMessage.fromRecipient.id
|
||||
val validGroup = groupId == targetThreadRecipient.groupId.orNull()
|
||||
val validTarget = !originalMessage.isViewOnce && !originalMessage.hasAudio() && !originalMessage.hasSharedContact()
|
||||
|
||||
if (!validTiming || !validAuthor || !validGroup || !validTarget) {
|
||||
warn(envelope.timestamp, "[handleEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${originalMessage.serverTimestamp}, editAuthor: ${senderRecipient.id}, targetAuthor: ${originalMessage.fromRecipient.id}, editThread: ${threadRecipient.id}, targetThread: ${targetThreadRecipient.id}, validity: (timing: $validTiming, author: $validAuthor, group: $validGroup, target: $validTarget)")
|
||||
warn(envelope.timestamp!!, "[handleEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${originalMessage.serverTimestamp}, editAuthor: ${senderRecipient.id}, targetAuthor: ${originalMessage.fromRecipient.id}, editThread: ${threadRecipient.id}, targetThread: ${targetThreadRecipient.id}, validity: (timing: $validTiming, author: $validAuthor, group: $validGroup, target: $validTarget)")
|
||||
return
|
||||
}
|
||||
|
||||
if (groupId != null && MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp, content, metadata, groupId, message.groupV2, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
|
||||
warn(envelope.timestamp, "[handleEditMessage] Group processor indicated we should ignore this.")
|
||||
if (groupId != null && MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp!!, content, metadata, groupId, message.groupV2!!, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
|
||||
warn(envelope.timestamp!!, "[handleEditMessage] Group processor indicated we should ignore this.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ object EditMessageProcessor {
|
||||
|
||||
if (insertResult != null) {
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
ApplicationDependencies.getJobManager().add(SendDeliveryReceiptJob(senderRecipient.id, message.timestamp, MessageId(insertResult.messageId)))
|
||||
ApplicationDependencies.getJobManager().add(SendDeliveryReceiptJob(senderRecipient.id, message.timestamp!!, MessageId(insertResult.messageId)))
|
||||
}
|
||||
|
||||
if (targetMessage.expireStarted > 0) {
|
||||
@@ -122,9 +122,9 @@ object EditMessageProcessor {
|
||||
message: DataMessage,
|
||||
targetMessage: MediaMmsMessageRecord
|
||||
): InsertResult? {
|
||||
val messageRanges: BodyRangeList? = message.bodyRangesList.filter { it.hasStyle() }.toList().toBodyRangeList()
|
||||
val messageRanges: BodyRangeList? = message.bodyRanges.filter { it.mentionAci == null }.toList().toBodyRangeList()
|
||||
val targetQuote = targetMessage.quote
|
||||
val quote: QuoteModel? = if (targetQuote != null && message.hasQuote()) {
|
||||
val quote: QuoteModel? = if (targetQuote != null && message.quote != null) {
|
||||
QuoteModel(
|
||||
targetQuote.id,
|
||||
targetQuote.author,
|
||||
@@ -138,25 +138,25 @@ object EditMessageProcessor {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val attachments = message.attachmentsList.toPointersWithinLimit()
|
||||
val attachments = message.attachments.toPointersWithinLimit()
|
||||
attachments.filter {
|
||||
MediaUtil.SlideType.LONG_TEXT == MediaUtil.getSlideTypeFromContentType(it.contentType)
|
||||
}
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipientId,
|
||||
sentTimeMillis = message.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = message.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = targetMessage.dateReceived,
|
||||
expiresIn = targetMessage.expiresIn,
|
||||
isViewOnce = message.isViewOnce,
|
||||
isViewOnce = message.isViewOnce == true,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
body = message.body,
|
||||
groupId = groupId,
|
||||
attachments = attachments,
|
||||
quote = quote,
|
||||
sharedContacts = emptyList(),
|
||||
linkPreviews = DataMessageProcessor.getLinkPreviews(message.previewList, message.body ?: "", false),
|
||||
mentions = DataMessageProcessor.getMentions(message.bodyRangesList),
|
||||
linkPreviews = DataMessageProcessor.getLinkPreviews(message.preview, message.body ?: "", false),
|
||||
mentions = DataMessageProcessor.getMentions(message.bodyRanges),
|
||||
serverGuid = envelope.serverGuid,
|
||||
messageRanges = messageRanges,
|
||||
isPushMessage = true
|
||||
@@ -185,8 +185,8 @@ object EditMessageProcessor {
|
||||
var textMessage = IncomingTextMessage(
|
||||
senderRecipientId,
|
||||
metadata.sourceDeviceId,
|
||||
envelope.timestamp,
|
||||
envelope.timestamp,
|
||||
envelope.timestamp!!,
|
||||
envelope.timestamp!!,
|
||||
targetMessage.dateReceived,
|
||||
message.body,
|
||||
Optional.ofNullable(groupId),
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.Semaphore
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -264,17 +264,17 @@ class IncomingMessageObserver(private val context: Application) {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun processEnvelope(bufferedProtocolStore: BufferedProtocolStore, envelope: SignalServiceProtos.Envelope, serverDeliveredTimestamp: Long): List<FollowUpOperation>? {
|
||||
return when (envelope.type.number) {
|
||||
SignalServiceProtos.Envelope.Type.RECEIPT_VALUE -> {
|
||||
fun processEnvelope(bufferedProtocolStore: BufferedProtocolStore, envelope: Envelope, serverDeliveredTimestamp: Long): List<FollowUpOperation>? {
|
||||
return when (envelope.type) {
|
||||
Envelope.Type.RECEIPT -> {
|
||||
processReceipt(envelope)
|
||||
null
|
||||
}
|
||||
|
||||
SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
||||
SignalServiceProtos.Envelope.Type.CIPHERTEXT_VALUE,
|
||||
SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE,
|
||||
SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE -> {
|
||||
Envelope.Type.PREKEY_BUNDLE,
|
||||
Envelope.Type.CIPHERTEXT,
|
||||
Envelope.Type.UNIDENTIFIED_SENDER,
|
||||
Envelope.Type.PLAINTEXT_CONTENT -> {
|
||||
processMessage(bufferedProtocolStore, envelope, serverDeliveredTimestamp)
|
||||
}
|
||||
|
||||
@@ -285,12 +285,12 @@ class IncomingMessageObserver(private val context: Application) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun processMessage(bufferedProtocolStore: BufferedProtocolStore, envelope: SignalServiceProtos.Envelope, serverDeliveredTimestamp: Long): List<FollowUpOperation> {
|
||||
private fun processMessage(bufferedProtocolStore: BufferedProtocolStore, envelope: Envelope, serverDeliveredTimestamp: Long): List<FollowUpOperation> {
|
||||
val localReceiveMetric = SignalLocalMetrics.MessageReceive.start()
|
||||
val result = MessageDecryptor.decrypt(context, bufferedProtocolStore, envelope, serverDeliveredTimestamp)
|
||||
localReceiveMetric.onEnvelopeDecrypted()
|
||||
|
||||
SignalLocalMetrics.MessageLatency.onMessageReceived(envelope.serverTimestamp, serverDeliveredTimestamp, envelope.urgent)
|
||||
SignalLocalMetrics.MessageLatency.onMessageReceived(envelope.serverTimestamp!!, serverDeliveredTimestamp, envelope.urgent!!)
|
||||
when (result) {
|
||||
is MessageDecryptor.Result.Success -> {
|
||||
val job = PushProcessMessageJob.processOrDefer(messageContentProcessor, result, localReceiveMetric)
|
||||
@@ -303,7 +303,7 @@ class IncomingMessageObserver(private val context: Application) {
|
||||
PushProcessMessageErrorJob(
|
||||
result.toMessageState(),
|
||||
result.errorMetadata.toExceptionMetadata(),
|
||||
result.envelope.timestamp
|
||||
result.envelope.timestamp!!
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -318,17 +318,17 @@ class IncomingMessageObserver(private val context: Application) {
|
||||
return result.followUpOperations
|
||||
}
|
||||
|
||||
private fun processReceipt(envelope: SignalServiceProtos.Envelope) {
|
||||
private fun processReceipt(envelope: Envelope) {
|
||||
if (!UuidUtil.isUuid(envelope.sourceServiceId)) {
|
||||
Log.w(TAG, "Invalid envelope source UUID!")
|
||||
return
|
||||
}
|
||||
|
||||
val senderId = RecipientId.from(ServiceId.parseOrThrow(envelope.sourceServiceId))
|
||||
val senderId = RecipientId.from(ServiceId.parseOrThrow(envelope.sourceServiceId!!))
|
||||
|
||||
Log.i(TAG, "Received server receipt. Sender: $senderId, Device: ${envelope.sourceDevice}, Timestamp: ${envelope.timestamp}")
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(envelope.timestamp, senderId, System.currentTimeMillis())
|
||||
SignalDatabase.messageLog.deleteEntryForRecipient(envelope.timestamp, senderId, envelope.sourceDevice)
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(envelope.timestamp!!, senderId, System.currentTimeMillis())
|
||||
SignalDatabase.messageLog.deleteEntryForRecipient(envelope.timestamp!!, senderId, envelope.sourceDevice!!)
|
||||
}
|
||||
|
||||
private fun MessageDecryptor.Result.toMessageState(): MessageState {
|
||||
|
||||
@@ -51,10 +51,11 @@ import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage
|
||||
import org.whispersystems.signalservice.internal.push.CallMessage
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.TypingMessage
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
|
||||
@@ -123,18 +124,18 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
|
||||
@Throws(BadGroupIdException::class)
|
||||
private fun getMessageDestination(content: Content, sender: Recipient): Recipient {
|
||||
return if (content.hasStoryMessage() && content.storyMessage.group.isValid) {
|
||||
getGroupRecipient(content.storyMessage.group, sender)
|
||||
return if (content.storyMessage != null && content.storyMessage!!.group.isValid) {
|
||||
getGroupRecipient(content.storyMessage!!.group, sender)
|
||||
} else if (content.dataMessage.hasGroupContext) {
|
||||
getGroupRecipient(content.dataMessage.groupV2, sender)
|
||||
} else if (content.editMessage.dataMessage.hasGroupContext) {
|
||||
getGroupRecipient(content.editMessage.dataMessage.groupV2, sender)
|
||||
getGroupRecipient(content.dataMessage!!.groupV2, sender)
|
||||
} else if (content.editMessage?.dataMessage.hasGroupContext) {
|
||||
getGroupRecipient(content.editMessage!!.dataMessage!!.groupV2, sender)
|
||||
} else {
|
||||
sender
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGroupRecipient(groupContextV2: SignalServiceProtos.GroupContextV2?, senderRecipient: Recipient): Recipient {
|
||||
private fun getGroupRecipient(groupContextV2: GroupContextV2?, senderRecipient: Recipient): Recipient {
|
||||
return if (groupContextV2 != null) {
|
||||
Recipient.externalPossiblyMigratedGroup(GroupId.v2(groupContextV2.groupMasterKey))
|
||||
} else {
|
||||
@@ -144,8 +145,8 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
|
||||
@Throws(BadGroupIdException::class)
|
||||
private fun shouldIgnore(content: Content, senderRecipient: Recipient, threadRecipient: Recipient): Boolean {
|
||||
if (content.hasDataMessage()) {
|
||||
val message = content.dataMessage
|
||||
if (content.dataMessage != null) {
|
||||
val message = content.dataMessage!!
|
||||
return if (threadRecipient.isGroup && threadRecipient.isBlocked) {
|
||||
true
|
||||
} else if (threadRecipient.isGroup) {
|
||||
@@ -153,7 +154,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
return senderRecipient.isBlocked
|
||||
}
|
||||
|
||||
val isTextMessage = message.hasBody()
|
||||
val isTextMessage = message.body != null
|
||||
val isMediaMessage = message.isMediaMessage
|
||||
val isExpireMessage = message.isExpirationUpdate
|
||||
val isGv2Update = message.hasSignedGroupChange
|
||||
@@ -164,15 +165,15 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
} else {
|
||||
senderRecipient.isBlocked
|
||||
}
|
||||
} else if (content.hasCallMessage()) {
|
||||
} else if (content.callMessage != null) {
|
||||
return senderRecipient.isBlocked
|
||||
} else if (content.hasTypingMessage()) {
|
||||
} else if (content.typingMessage != null) {
|
||||
if (senderRecipient.isBlocked) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (content.typingMessage.hasGroupId()) {
|
||||
val groupId: GroupId = GroupId.push(content.typingMessage.groupId)
|
||||
if (content.typingMessage!!.groupId != null) {
|
||||
val groupId: GroupId = GroupId.push(content.typingMessage!!.groupId!!)
|
||||
val groupRecipient = Recipient.externalPossiblyMigratedGroup(groupId)
|
||||
return if (groupRecipient.isBlocked || !groupRecipient.isActiveGroup) {
|
||||
true
|
||||
@@ -181,7 +182,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
groupRecord.isPresent && groupRecord.get().isAnnouncementGroup && !groupRecord.get().admins.contains(senderRecipient)
|
||||
}
|
||||
}
|
||||
} else if (content.hasStoryMessage()) {
|
||||
} else if (content.storyMessage != null) {
|
||||
return if (threadRecipient.isGroup && threadRecipient.isBlocked) {
|
||||
true
|
||||
} else {
|
||||
@@ -227,7 +228,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
content: Content,
|
||||
metadata: EnvelopeMetadata,
|
||||
groupId: GroupId.V2,
|
||||
groupV2: SignalServiceProtos.GroupContextV2,
|
||||
groupV2: GroupContextV2,
|
||||
senderRecipient: Recipient,
|
||||
groupSecretParams: GroupSecretParams? = null
|
||||
): Gv2PreProcessResult {
|
||||
@@ -254,12 +255,12 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
}
|
||||
|
||||
if (groupRecord.isPresent && groupRecord.get().isAnnouncementGroup && !groupRecord.get().admins.contains(senderRecipient)) {
|
||||
if (content.hasDataMessage()) {
|
||||
if (content.dataMessage.hasDisallowedAnnouncementOnlyContent) {
|
||||
if (content.dataMessage != null) {
|
||||
if (content.dataMessage!!.hasDisallowedAnnouncementOnlyContent) {
|
||||
Log.w(TAG, "Ignoring message from ${senderRecipient.id} because it has disallowed content, and they're not an admin in an announcement-only group.")
|
||||
return Gv2PreProcessResult.IGNORE
|
||||
}
|
||||
} else if (content.hasTypingMessage()) {
|
||||
} else if (content.typingMessage != null) {
|
||||
Log.w(TAG, "Ignoring typing indicator from ${senderRecipient.id} because they're not an admin in an announcement-only group.")
|
||||
return Gv2PreProcessResult.IGNORE
|
||||
}
|
||||
@@ -275,14 +276,19 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
fun updateGv2GroupFromServerOrP2PChange(
|
||||
context: Context,
|
||||
timestamp: Long,
|
||||
groupV2: SignalServiceProtos.GroupContextV2,
|
||||
groupV2: GroupContextV2,
|
||||
localRecord: Optional<GroupRecord>,
|
||||
groupSecretParams: GroupSecretParams? = null
|
||||
): GroupsV2StateProcessor.GroupUpdateResult? {
|
||||
return try {
|
||||
val signedGroupChange: ByteArray? = if (groupV2.hasSignedGroupChange) groupV2.signedGroupChange else null
|
||||
val updatedTimestamp = if (signedGroupChange != null) timestamp else timestamp - 1
|
||||
GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, localRecord, groupSecretParams, groupV2.revision, updatedTimestamp, signedGroupChange)
|
||||
if (groupV2.revision != null) {
|
||||
GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, localRecord, groupSecretParams, groupV2.revision!!, updatedTimestamp, signedGroupChange)
|
||||
} else {
|
||||
warn(timestamp, "Ignore group update message without a revision")
|
||||
null
|
||||
}
|
||||
} catch (e: GroupNotAMemberException) {
|
||||
warn(timestamp, "Ignoring message for a group we're not in")
|
||||
null
|
||||
@@ -331,11 +337,11 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
|
||||
val earlyCacheEntries: List<EarlyMessageCacheEntry>? = ApplicationDependencies
|
||||
.getEarlyMessageCache()
|
||||
.retrieve(senderRecipient.id, envelope.timestamp)
|
||||
.retrieve(senderRecipient.id, envelope.timestamp!!)
|
||||
.orNull()
|
||||
|
||||
if (!processingEarlyContent && earlyCacheEntries != null) {
|
||||
log(envelope.timestamp, "Found " + earlyCacheEntries.size + " dependent item(s) that were retrieved earlier. Processing.")
|
||||
log(envelope.timestamp!!, "Found " + earlyCacheEntries.size + " dependent item(s) that were retrieved earlier. Processing.")
|
||||
for (entry in earlyCacheEntries) {
|
||||
handleMessage(senderRecipient, entry.envelope, entry.content, entry.metadata, entry.serverDeliveredTimestamp, processingEarlyContent = true, localMetric = null)
|
||||
}
|
||||
@@ -411,17 +417,17 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
val threadRecipient = getMessageDestination(content, senderRecipient)
|
||||
|
||||
if (shouldIgnore(content, senderRecipient, threadRecipient)) {
|
||||
log(envelope.timestamp, "Ignoring message.")
|
||||
log(envelope.timestamp!!, "Ignoring message.")
|
||||
return
|
||||
}
|
||||
|
||||
val pending: PendingRetryReceiptModel? = ApplicationDependencies.getPendingRetryReceiptCache().get(senderRecipient.id, envelope.timestamp)
|
||||
val receivedTime: Long = handlePendingRetry(pending, envelope.timestamp, threadRecipient)
|
||||
val pending: PendingRetryReceiptModel? = ApplicationDependencies.getPendingRetryReceiptCache().get(senderRecipient.id, envelope.timestamp!!)
|
||||
val receivedTime: Long = handlePendingRetry(pending, envelope.timestamp!!, threadRecipient)
|
||||
|
||||
log(envelope.timestamp, "Beginning message processing. Sender: " + formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId))
|
||||
log(envelope.timestamp!!, "Beginning message processing. Sender: " + formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId))
|
||||
localMetric?.onPreProcessComplete()
|
||||
when {
|
||||
content.hasDataMessage() -> {
|
||||
content.dataMessage != null -> {
|
||||
DataMessageProcessor.process(
|
||||
context,
|
||||
senderRecipient,
|
||||
@@ -435,7 +441,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
content.hasSyncMessage() -> {
|
||||
content.syncMessage != null -> {
|
||||
TextSecurePreferences.setMultiDevice(context, true)
|
||||
|
||||
SyncMessageProcessor.process(
|
||||
@@ -448,21 +454,20 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
content.hasCallMessage() -> {
|
||||
log(envelope.timestamp, "Got call message...")
|
||||
content.callMessage != null -> {
|
||||
log(envelope.timestamp!!, "Got call message...")
|
||||
|
||||
val message: SignalServiceProtos.CallMessage = content.callMessage
|
||||
val destinationDeviceId: Int? = if (message.hasDestinationDeviceId()) message.destinationDeviceId else null
|
||||
val message: CallMessage = content.callMessage!!
|
||||
|
||||
if (destinationDeviceId != null && destinationDeviceId != SignalStore.account().deviceId) {
|
||||
log(envelope.timestamp, "Ignoring call message that is not for this device! intended: $destinationDeviceId, this: ${SignalStore.account().deviceId}")
|
||||
if (message.destinationDeviceId != null && message.destinationDeviceId != SignalStore.account().deviceId) {
|
||||
log(envelope.timestamp!!, "Ignoring call message that is not for this device! intended: ${message.destinationDeviceId}, this: ${SignalStore.account().deviceId}")
|
||||
return
|
||||
}
|
||||
|
||||
CallMessageProcessor.process(senderRecipient, envelope, content, metadata, serverDeliveredTimestamp)
|
||||
}
|
||||
|
||||
content.hasReceiptMessage() -> {
|
||||
content.receiptMessage != null -> {
|
||||
ReceiptMessageProcessor.process(
|
||||
context,
|
||||
senderRecipient,
|
||||
@@ -473,11 +478,11 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
content.hasTypingMessage() -> {
|
||||
handleTypingMessage(envelope, metadata, content.typingMessage, senderRecipient)
|
||||
content.typingMessage != null -> {
|
||||
handleTypingMessage(envelope, metadata, content.typingMessage!!, senderRecipient)
|
||||
}
|
||||
|
||||
content.hasStoryMessage() -> {
|
||||
content.storyMessage != null -> {
|
||||
StoryMessageProcessor.process(
|
||||
envelope,
|
||||
content,
|
||||
@@ -487,11 +492,11 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
content.hasDecryptionErrorMessage() -> {
|
||||
content.decryptionErrorMessage != null -> {
|
||||
handleRetryReceipt(envelope, metadata, content.decryptionErrorMessage!!.toDecryptionErrorMessage(metadata), senderRecipient)
|
||||
}
|
||||
|
||||
content.hasEditMessage() -> {
|
||||
content.editMessage != null -> {
|
||||
EditMessageProcessor.process(
|
||||
context,
|
||||
senderRecipient,
|
||||
@@ -503,17 +508,17 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
content.hasSenderKeyDistributionMessage() || content.hasPniSignatureMessage() -> {
|
||||
content.senderKeyDistributionMessage != null || content.pniSignatureMessage != null -> {
|
||||
// Already handled, here in order to prevent unrecognized message log
|
||||
}
|
||||
|
||||
else -> {
|
||||
warn(envelope.timestamp, "Got unrecognized message!")
|
||||
warn(envelope.timestamp!!, "Got unrecognized message!")
|
||||
}
|
||||
}
|
||||
|
||||
if (pending != null) {
|
||||
warn(envelope.timestamp, "Pending retry was processed. Deleting.")
|
||||
warn(envelope.timestamp!!, "Pending retry was processed. Deleting.")
|
||||
ApplicationDependencies.getPendingRetryReceiptCache().delete(pending)
|
||||
}
|
||||
}
|
||||
@@ -529,10 +534,10 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
return
|
||||
}
|
||||
|
||||
val threadId: Long = if (typingMessage.hasGroupId()) {
|
||||
val groupId = GroupId.push(typingMessage.groupId)
|
||||
val threadId: Long = if (typingMessage.groupId != null) {
|
||||
val groupId = GroupId.push(typingMessage.groupId!!)
|
||||
if (!SignalDatabase.groups.isCurrentMember(groupId, senderRecipient.id)) {
|
||||
warn(envelope.timestamp, "Seen typing indicator for non-member " + senderRecipient.id)
|
||||
warn(envelope.timestamp!!, "Seen typing indicator for non-member " + senderRecipient.id)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -543,7 +548,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
}
|
||||
|
||||
if (threadId <= 0) {
|
||||
warn(envelope.timestamp, "Couldn't find a matching thread for a typing message.")
|
||||
warn(envelope.timestamp!!, "Couldn't find a matching thread for a typing message.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -558,19 +563,19 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
|
||||
private fun handleRetryReceipt(envelope: Envelope, metadata: EnvelopeMetadata, decryptionErrorMessage: DecryptionErrorMessage, senderRecipient: Recipient) {
|
||||
if (!FeatureFlags.retryReceipts()) {
|
||||
warn(envelope.timestamp, "[RetryReceipt] Feature flag disabled, skipping retry receipt.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt] Feature flag disabled, skipping retry receipt.")
|
||||
return
|
||||
}
|
||||
|
||||
if (decryptionErrorMessage.deviceId != SignalStore.account().deviceId) {
|
||||
log(envelope.timestamp, "[RetryReceipt] Received a DecryptionErrorMessage targeting a linked device. Ignoring.")
|
||||
log(envelope.timestamp!!, "[RetryReceipt] Received a DecryptionErrorMessage targeting a linked device. Ignoring.")
|
||||
return
|
||||
}
|
||||
|
||||
val sentTimestamp = decryptionErrorMessage.timestamp
|
||||
warn(envelope.timestamp, "[RetryReceipt] Received a retry receipt from ${formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId)} for message with timestamp $sentTimestamp.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt] Received a retry receipt from ${formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId)} for message with timestamp $sentTimestamp.")
|
||||
if (!senderRecipient.hasServiceId()) {
|
||||
warn(envelope.timestamp, "[RetryReceipt] Requester ${senderRecipient.id} somehow has no UUID! timestamp: $sentTimestamp")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt] Requester ${senderRecipient.id} somehow has no UUID! timestamp: $sentTimestamp")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -593,18 +598,18 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
val relatedMessage = findRetryReceiptRelatedMessage(messageLogEntry, sentTimestamp)
|
||||
|
||||
if (relatedMessage == null) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-SK] The related message could not be found! There shouldn't be any sender key resends where we can't find the related message. Skipping.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-SK] The related message could not be found! There shouldn't be any sender key resends where we can't find the related message. Skipping.")
|
||||
return
|
||||
}
|
||||
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(relatedMessage.threadId)
|
||||
if (threadRecipient == null) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-SK] Could not find a thread recipient! Skipping.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-SK] Could not find a thread recipient! Skipping.")
|
||||
return
|
||||
}
|
||||
|
||||
if (!threadRecipient.isPushV2Group && !threadRecipient.isDistributionList) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-SK] Thread recipient is not a V2 group or distribution list! Skipping.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-SK] Thread recipient is not a V2 group or distribution list! Skipping.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -628,7 +633,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
SignalDatabase.senderKeyShared.delete(distributionId, setOf(requesterAddress))
|
||||
|
||||
if (messageLogEntry != null) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-SK] Found MSL entry for ${requester.id} ($requesterAddress) with timestamp $sentTimestamp. Scheduling a resend.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-SK] Found MSL entry for ${requester.id} ($requesterAddress) with timestamp $sentTimestamp. Scheduling a resend.")
|
||||
ApplicationDependencies.getJobManager().add(
|
||||
ResendMessageJob(
|
||||
messageLogEntry.recipientId,
|
||||
@@ -641,7 +646,7 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
)
|
||||
} else {
|
||||
warn(envelope.timestamp, "[RetryReceipt-SK] Unable to find MSL entry for ${requester.id} ($requesterAddress) with timestamp $sentTimestamp for ${if (groupId != null) "group $groupId" else "distribution list"}. Scheduling a job to send them the SenderKeyDistributionMessage. Membership will be checked there.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-SK] Unable to find MSL entry for ${requester.id} ($requesterAddress) with timestamp $sentTimestamp for ${if (groupId != null) "group $groupId" else "distribution list"}. Scheduling a job to send them the SenderKeyDistributionMessage. Membership will be checked there.")
|
||||
ApplicationDependencies.getJobManager().add(SenderKeyDistributionSendJob(requester.id, threadRecipient.id))
|
||||
}
|
||||
}
|
||||
@@ -653,13 +658,13 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
if (decryptionErrorMessage.ratchetKey.isPresent &&
|
||||
ratchetKeyMatches(requester, metadata.sourceDeviceId, decryptionErrorMessage.ratchetKey.get())
|
||||
) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
|
||||
ApplicationDependencies.getProtocolStore().aci().sessions().archiveSession(requester.requireServiceId(), metadata.sourceDeviceId)
|
||||
archivedSession = true
|
||||
}
|
||||
|
||||
if (messageLogEntry != null) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-I] Found an entry in the MSL. Resending.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Found an entry in the MSL. Resending.")
|
||||
ApplicationDependencies.getJobManager().add(
|
||||
ResendMessageJob(
|
||||
messageLogEntry.recipientId,
|
||||
@@ -672,10 +677,10 @@ open class MessageContentProcessor(private val context: Context) {
|
||||
)
|
||||
)
|
||||
} else if (archivedSession) {
|
||||
warn(envelope.timestamp, "[RetryReceipt-I] Could not find an entry in the MSL, but we archived the session, so we're sending a null message to complete the reset.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Could not find an entry in the MSL, but we archived the session, so we're sending a null message to complete the reset.")
|
||||
ApplicationDependencies.getJobManager().add(NullMessageSendJob(requester.id))
|
||||
} else {
|
||||
warn(envelope.timestamp, "[RetryReceipt-I] Could not find an entry in the MSL. Skipping.")
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Could not find an entry in the MSL. Skipping.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,9 +60,9 @@ import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.PNI
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.PniSignatureMessage
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.PniSignatureMessage
|
||||
import java.util.Optional
|
||||
import kotlin.time.Duration.Companion.nanoseconds
|
||||
import kotlin.time.DurationUnit
|
||||
@@ -103,7 +103,7 @@ object MessageDecryptor {
|
||||
return Result.Ignore(envelope, serverDeliveredTimestamp, emptyList())
|
||||
}
|
||||
|
||||
if (destination == selfPni && envelope.hasSourceServiceId()) {
|
||||
if (destination == selfPni && envelope.sourceServiceId != null) {
|
||||
Log.i(TAG, "${logPrefix(envelope)} Received a message at our PNI. Marking as needing a PNI signature.")
|
||||
|
||||
val sourceServiceId = ServiceId.parseOrNull(envelope.sourceServiceId)
|
||||
@@ -116,7 +116,7 @@ object MessageDecryptor {
|
||||
}
|
||||
}
|
||||
|
||||
if (destination == selfPni && !envelope.hasSourceServiceId()) {
|
||||
if (destination == selfPni && envelope.sourceServiceId == null) {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Got a sealed sender message to our PNI? Invalid message, ignoring.")
|
||||
return Result.Ignore(envelope, serverDeliveredTimestamp, emptyList())
|
||||
}
|
||||
@@ -143,7 +143,7 @@ object MessageDecryptor {
|
||||
return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList())
|
||||
}
|
||||
|
||||
Log.d(TAG, "${logPrefix(envelope, cipherResult)} Successfully decrypted the envelope in ${(endTimeNanos - startTimeNanos).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2)} ms (GUID ${envelope.serverGuid}). Delivery latency: ${serverDeliveredTimestamp - envelope.serverTimestamp} ms, Urgent: ${envelope.urgent}")
|
||||
Log.d(TAG, "${logPrefix(envelope, cipherResult)} Successfully decrypted the envelope in ${(endTimeNanos - startTimeNanos).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2)} ms (GUID ${envelope.serverGuid}). Delivery latency: ${serverDeliveredTimestamp - envelope.serverTimestamp!!} ms, Urgent: ${envelope.urgent}")
|
||||
|
||||
val validationResult: EnvelopeContentValidator.Result = EnvelopeContentValidator.validate(envelope, cipherResult.content)
|
||||
|
||||
@@ -163,17 +163,17 @@ object MessageDecryptor {
|
||||
}
|
||||
|
||||
// Must handle SKDM's immediately, because subsequent decryptions could rely on it
|
||||
if (cipherResult.content.hasSenderKeyDistributionMessage()) {
|
||||
if (cipherResult.content.senderKeyDistributionMessage != null) {
|
||||
handleSenderKeyDistributionMessage(
|
||||
envelope,
|
||||
cipherResult.metadata.sourceServiceId,
|
||||
cipherResult.metadata.sourceDeviceId,
|
||||
SenderKeyDistributionMessage(cipherResult.content.senderKeyDistributionMessage.toByteArray()),
|
||||
SenderKeyDistributionMessage(cipherResult.content.senderKeyDistributionMessage!!.toByteArray()),
|
||||
bufferedProtocolStore.getAciStore()
|
||||
)
|
||||
}
|
||||
|
||||
if (cipherResult.content.hasPniSignatureMessage()) {
|
||||
if (cipherResult.content.pniSignatureMessage != null) {
|
||||
if (cipherResult.metadata.sourceServiceId is ACI) {
|
||||
handlePniSignatureMessage(
|
||||
envelope,
|
||||
@@ -181,19 +181,19 @@ object MessageDecryptor {
|
||||
cipherResult.metadata.sourceServiceId as ACI,
|
||||
cipherResult.metadata.sourceE164,
|
||||
cipherResult.metadata.sourceDeviceId,
|
||||
cipherResult.content.pniSignatureMessage
|
||||
cipherResult.content.pniSignatureMessage!!
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the sourceServiceId isn't an ACI!")
|
||||
}
|
||||
} else if (cipherResult.content.hasPniSignatureMessage()) {
|
||||
} else if (cipherResult.content.pniSignatureMessage != null) {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the feature flag is disabled!")
|
||||
}
|
||||
|
||||
// TODO We can move this to the "message processing" stage once we give it access to the envelope. But for now it'll stay here.
|
||||
if (envelope.hasReportingToken() && envelope.reportingToken != null && envelope.reportingToken.size() > 0) {
|
||||
if (envelope.reportingToken != null && envelope.reportingToken!!.size > 0) {
|
||||
val sender = RecipientId.from(cipherResult.metadata.sourceServiceId)
|
||||
SignalDatabase.recipients.setReportingToken(sender, envelope.reportingToken.toByteArray())
|
||||
SignalDatabase.recipients.setReportingToken(sender, envelope.reportingToken!!.toByteArray())
|
||||
}
|
||||
|
||||
Result.Success(envelope, serverDeliveredTimestamp, cipherResult.content, cipherResult.metadata, followUpOperations.toUnmodifiableList())
|
||||
@@ -218,7 +218,7 @@ object MessageDecryptor {
|
||||
|
||||
followUpOperations += FollowUpOperation {
|
||||
val sender: Recipient = Recipient.external(context, e.sender)
|
||||
AutomaticSessionResetJob(sender.id, e.senderDevice, envelope.timestamp)
|
||||
AutomaticSessionResetJob(sender.id, e.senderDevice, envelope.timestamp!!)
|
||||
}
|
||||
|
||||
Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList())
|
||||
@@ -281,7 +281,7 @@ object MessageDecryptor {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Decryption error for a sync message! Enqueuing a session reset job.")
|
||||
|
||||
followUpOperations += FollowUpOperation {
|
||||
AutomaticSessionResetJob(sender.id, senderDevice, envelope.timestamp)
|
||||
AutomaticSessionResetJob(sender.id, senderDevice, envelope.timestamp!!)
|
||||
}
|
||||
|
||||
return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations)
|
||||
@@ -309,7 +309,7 @@ object MessageDecryptor {
|
||||
SignalDatabase.threads.getOrCreateThreadIdFor(sender)
|
||||
}
|
||||
|
||||
ApplicationDependencies.getPendingRetryReceiptCache().insert(sender.id, senderDevice, envelope.timestamp, receivedTimestamp, threadId)
|
||||
ApplicationDependencies.getPendingRetryReceiptCache().insert(sender.id, senderDevice, envelope.timestamp!!, receivedTimestamp, threadId)
|
||||
ApplicationDependencies.getPendingRetryReceiptManager().scheduleIfNecessary()
|
||||
null
|
||||
}
|
||||
@@ -334,7 +334,7 @@ object MessageDecryptor {
|
||||
private fun handlePniSignatureMessage(envelope: Envelope, protocolStore: BufferedProtocolStore, aci: ACI, e164: String?, deviceId: Int, pniSignatureMessage: PniSignatureMessage) {
|
||||
Log.i(TAG, "${logPrefix(envelope, aci)} Processing PniSignatureMessage")
|
||||
|
||||
val pni: PNI = PNI.parseOrThrow(pniSignatureMessage.pni.toByteArray())
|
||||
val pni: PNI = PNI.parseOrThrow(pniSignatureMessage.pni!!.toByteArray())
|
||||
|
||||
if (SignalDatabase.recipients.isAssociated(aci, pni)) {
|
||||
Log.i(TAG, "${logPrefix(envelope, aci)}[handlePniSignatureMessage] ACI ($aci) and PNI ($pni) are already associated.")
|
||||
@@ -356,7 +356,7 @@ object MessageDecryptor {
|
||||
return
|
||||
}
|
||||
|
||||
if (pniIdentity.verifyAlternateIdentity(aciIdentity, pniSignatureMessage.signature.toByteArray())) {
|
||||
if (pniIdentity.verifyAlternateIdentity(aciIdentity, pniSignatureMessage.signature!!.toByteArray())) {
|
||||
Log.i(TAG, "${logPrefix(envelope, aci)}[validatePniSignature] PNI signature is valid. Associating ACI ($aci) with PNI ($pni)")
|
||||
SignalDatabase.recipients.getAndPossiblyMergePnpVerified(aci, pni, e164)
|
||||
} else {
|
||||
@@ -387,28 +387,28 @@ object MessageDecryptor {
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope): String {
|
||||
return logPrefix(envelope.timestamp, envelope.sourceServiceId ?: "<sealed>", envelope.sourceDevice)
|
||||
return logPrefix(envelope.timestamp!!, envelope.sourceServiceId ?: "<sealed>", envelope.sourceDevice)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, sender: ServiceId): String {
|
||||
return logPrefix(envelope.timestamp, sender.toString(), envelope.sourceDevice)
|
||||
return logPrefix(envelope.timestamp!!, sender.toString(), envelope.sourceDevice)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, cipherResult: SignalServiceCipherResult): String {
|
||||
return logPrefix(envelope.timestamp, cipherResult.metadata.sourceServiceId.toString(), cipherResult.metadata.sourceDeviceId)
|
||||
return logPrefix(envelope.timestamp!!, cipherResult.metadata.sourceServiceId.toString(), cipherResult.metadata.sourceDeviceId)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, exception: ProtocolException): String {
|
||||
return if (exception.sender != null) {
|
||||
logPrefix(envelope.timestamp, exception.sender, exception.senderDevice)
|
||||
logPrefix(envelope.timestamp!!, exception.sender, exception.senderDevice)
|
||||
} else {
|
||||
logPrefix(envelope.timestamp, envelope.sourceServiceId, envelope.sourceDevice)
|
||||
logPrefix(envelope.timestamp!!, envelope.sourceServiceId, envelope.sourceDevice)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logPrefix(timestamp: Long, sender: String?, deviceId: Int): String {
|
||||
private fun logPrefix(timestamp: Long, sender: String?, deviceId: Int?): String {
|
||||
val senderString = sender ?: "null"
|
||||
return "[$timestamp] $senderString:$deviceId |"
|
||||
return "[$timestamp] $senderString:${deviceId ?: 0} |"
|
||||
}
|
||||
|
||||
private fun buildSendRetryReceiptJob(envelope: Envelope, protocolException: ProtocolException, sender: Recipient): SendRetryReceiptJob {
|
||||
@@ -419,11 +419,11 @@ object MessageDecryptor {
|
||||
originalContent = protocolException.unidentifiedSenderMessageContent.get().content
|
||||
envelopeType = protocolException.unidentifiedSenderMessageContent.get().type
|
||||
} else {
|
||||
originalContent = envelope.content.toByteArray()
|
||||
envelopeType = envelope.type.number.toCiphertextMessageType()
|
||||
originalContent = envelope.content!!.toByteArray()
|
||||
envelopeType = envelope.type!!.value.toCiphertextMessageType()
|
||||
}
|
||||
|
||||
val decryptionErrorMessage: DecryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(originalContent, envelopeType, envelope.timestamp, protocolException.senderDevice)
|
||||
val decryptionErrorMessage: DecryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(originalContent, envelopeType, envelope.timestamp!!, protocolException.senderDevice)
|
||||
val groupId: GroupId? = protocolException.parseGroupId(envelope)
|
||||
return SendRetryReceiptJob(sender.id, Optional.ofNullable(groupId), decryptionErrorMessage)
|
||||
}
|
||||
@@ -443,10 +443,10 @@ object MessageDecryptor {
|
||||
|
||||
private fun Int.toCiphertextMessageType(): Int {
|
||||
return when (this) {
|
||||
Envelope.Type.CIPHERTEXT_VALUE -> CiphertextMessage.WHISPER_TYPE
|
||||
Envelope.Type.PREKEY_BUNDLE_VALUE -> CiphertextMessage.PREKEY_TYPE
|
||||
Envelope.Type.UNIDENTIFIED_SENDER_VALUE -> CiphertextMessage.SENDERKEY_TYPE
|
||||
Envelope.Type.PLAINTEXT_CONTENT_VALUE -> CiphertextMessage.PLAINTEXT_CONTENT_TYPE
|
||||
Envelope.Type.CIPHERTEXT.value -> CiphertextMessage.WHISPER_TYPE
|
||||
Envelope.Type.PREKEY_BUNDLE.value -> CiphertextMessage.PREKEY_TYPE
|
||||
Envelope.Type.UNIDENTIFIED_SENDER.value -> CiphertextMessage.SENDERKEY_TYPE
|
||||
Envelope.Type.PLAINTEXT_CONTENT.value -> CiphertextMessage.PLAINTEXT_CONTENT_TYPE
|
||||
else -> CiphertextMessage.WHISPER_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.EarlyMessageCacheEntry
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.ReceiptMessage
|
||||
|
||||
object ReceiptMessageProcessor {
|
||||
fun process(context: Context, senderRecipient: Recipient, envelope: Envelope, content: SignalServiceProtos.Content, metadata: EnvelopeMetadata, earlyMessageCacheEntry: EarlyMessageCacheEntry?) {
|
||||
val receiptMessage = content.receiptMessage
|
||||
fun process(context: Context, senderRecipient: Recipient, envelope: Envelope, content: Content, metadata: EnvelopeMetadata, earlyMessageCacheEntry: EarlyMessageCacheEntry?) {
|
||||
val receiptMessage = content.receiptMessage!!
|
||||
|
||||
when (receiptMessage.type) {
|
||||
ReceiptMessage.Type.DELIVERY -> handleDeliveryReceipt(envelope, metadata, receiptMessage, senderRecipient.id)
|
||||
ReceiptMessage.Type.READ -> handleReadReceipt(context, senderRecipient.id, envelope, metadata, receiptMessage, earlyMessageCacheEntry)
|
||||
ReceiptMessage.Type.VIEWED -> handleViewedReceipt(context, envelope, metadata, receiptMessage, senderRecipient.id, earlyMessageCacheEntry)
|
||||
else -> warn(envelope.timestamp, "Unknown recipient message type ${receiptMessage.type}")
|
||||
else -> warn(envelope.timestamp!!, "Unknown recipient message type ${receiptMessage.type}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ object ReceiptMessageProcessor {
|
||||
deliveryReceipt: ReceiptMessage,
|
||||
senderRecipientId: RecipientId
|
||||
) {
|
||||
log(envelope.timestamp, "Processing delivery receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${deliveryReceipt.timestampList.joinToString(", ")}")
|
||||
log(envelope.timestamp!!, "Processing delivery receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${deliveryReceipt.timestamp.joinToString(", ")}")
|
||||
|
||||
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementDeliveryReceiptCounts(deliveryReceipt.timestampList, senderRecipientId, envelope.timestamp)
|
||||
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementDeliveryReceiptCounts(deliveryReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
|
||||
|
||||
for (targetTimestamp in missingTargetTimestamps) {
|
||||
warn(envelope.timestamp, "[handleDeliveryReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId")
|
||||
warn(envelope.timestamp!!, "[handleDeliveryReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId")
|
||||
// Early delivery receipts are special-cased in the database methods
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ object ReceiptMessageProcessor {
|
||||
PushProcessEarlyMessagesJob.enqueue()
|
||||
}
|
||||
|
||||
SignalDatabase.pendingPniSignatureMessages.acknowledgeReceipts(senderRecipientId, deliveryReceipt.timestampList, metadata.sourceDeviceId)
|
||||
SignalDatabase.messageLog.deleteEntriesForRecipient(deliveryReceipt.timestampList, senderRecipientId, metadata.sourceDeviceId)
|
||||
SignalDatabase.pendingPniSignatureMessages.acknowledgeReceipts(senderRecipientId, deliveryReceipt.timestamp, metadata.sourceDeviceId)
|
||||
SignalDatabase.messageLog.deleteEntriesForRecipient(deliveryReceipt.timestamp, senderRecipientId, metadata.sourceDeviceId)
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@@ -63,19 +63,19 @@ object ReceiptMessageProcessor {
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
) {
|
||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
log(envelope.timestamp, "Ignoring read receipts for IDs: " + readReceipt.timestampList.joinToString(", "))
|
||||
log(envelope.timestamp!!, "Ignoring read receipts for IDs: " + readReceipt.timestamp.joinToString(", "))
|
||||
return
|
||||
}
|
||||
|
||||
log(envelope.timestamp, "Processing read receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${readReceipt.timestampList.joinToString(", ")}")
|
||||
log(envelope.timestamp!!, "Processing read receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${readReceipt.timestamp.joinToString(", ")}")
|
||||
|
||||
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementReadReceiptCounts(readReceipt.timestampList, senderRecipientId, envelope.timestamp)
|
||||
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementReadReceiptCounts(readReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
|
||||
|
||||
if (missingTargetTimestamps.isNotEmpty()) {
|
||||
val selfId = Recipient.self().id
|
||||
|
||||
for (targetTimestamp in missingTargetTimestamps) {
|
||||
warn(envelope.timestamp, "[handleReadReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt, so associating with message from self ($selfId)")
|
||||
warn(envelope.timestamp!!, "[handleReadReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt, so associating with message from self ($selfId)")
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(selfId, targetTimestamp, earlyMessageCacheEntry)
|
||||
}
|
||||
@@ -99,28 +99,28 @@ object ReceiptMessageProcessor {
|
||||
val storyViewedReceipts = SignalStore.storyValues().viewedReceiptsEnabled
|
||||
|
||||
if (!readReceipts && !storyViewedReceipts) {
|
||||
log(envelope.timestamp, "Ignoring viewed receipts for IDs: ${viewedReceipt.timestampList.joinToString(", ")}")
|
||||
log(envelope.timestamp!!, "Ignoring viewed receipts for IDs: ${viewedReceipt.timestamp.joinToString(", ")}")
|
||||
return
|
||||
}
|
||||
|
||||
log(envelope.timestamp, "Processing viewed receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Only Stories: ${!readReceipts}, Timestamps: ${viewedReceipt.timestampList.joinToString(", ")}")
|
||||
log(envelope.timestamp!!, "Processing viewed receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Only Stories: ${!readReceipts}, Timestamps: ${viewedReceipt.timestamp.joinToString(", ")}")
|
||||
|
||||
val missingTargetTimestamps: Set<Long> = if (readReceipts && storyViewedReceipts) {
|
||||
SignalDatabase.messages.incrementViewedReceiptCounts(viewedReceipt.timestampList, senderRecipientId, envelope.timestamp)
|
||||
SignalDatabase.messages.incrementViewedReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
|
||||
} else if (readReceipts) {
|
||||
SignalDatabase.messages.incrementViewedNonStoryReceiptCounts(viewedReceipt.timestampList, senderRecipientId, envelope.timestamp)
|
||||
SignalDatabase.messages.incrementViewedNonStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
|
||||
} else {
|
||||
SignalDatabase.messages.incrementViewedStoryReceiptCounts(viewedReceipt.timestampList, senderRecipientId, envelope.timestamp)
|
||||
SignalDatabase.messages.incrementViewedStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
|
||||
}
|
||||
|
||||
val foundTargetTimestamps: Set<Long> = viewedReceipt.timestampList.toSet() - missingTargetTimestamps.toSet()
|
||||
val foundTargetTimestamps: Set<Long> = viewedReceipt.timestamp.toSet() - missingTargetTimestamps.toSet()
|
||||
SignalDatabase.messages.updateViewedStories(foundTargetTimestamps)
|
||||
|
||||
if (missingTargetTimestamps.isNotEmpty()) {
|
||||
val selfId = Recipient.self().id
|
||||
|
||||
for (targetTimestamp in missingTargetTimestamps) {
|
||||
warn(envelope.timestamp, "[handleViewedReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt so associating with message from self ($selfId)")
|
||||
warn(envelope.timestamp!!, "[handleViewedReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt so associating with message from self ($selfId)")
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(selfId, targetTimestamp, earlyMessageCacheEntry)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import com.google.protobuf.GeneratedMessageLite
|
||||
import ProtoUtil.isNotEmpty
|
||||
import com.squareup.wire.Message
|
||||
import okio.ByteString
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.libsignal.protocol.message.DecryptionErrorMessage
|
||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
|
||||
@@ -18,79 +19,78 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin
|
||||
import org.whispersystems.signalservice.api.payments.Money
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Payment
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage
|
||||
import org.whispersystems.signalservice.internal.push.AttachmentPointer
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage.Payment
|
||||
import org.whispersystems.signalservice.internal.push.GroupContextV2
|
||||
import org.whispersystems.signalservice.internal.push.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Sent
|
||||
import org.whispersystems.signalservice.internal.push.TypingMessage
|
||||
import java.util.Optional
|
||||
|
||||
private val ByteString.isNotEmpty: Boolean
|
||||
get() = !this.isEmpty
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object SignalServiceProtoUtil {
|
||||
|
||||
/** Contains some user data that affects the conversation */
|
||||
val DataMessage.hasRenderableContent: Boolean
|
||||
get() {
|
||||
return attachmentsList.isNotEmpty() ||
|
||||
hasBody() ||
|
||||
hasQuote() ||
|
||||
contactList.isNotEmpty() ||
|
||||
previewList.isNotEmpty() ||
|
||||
bodyRangesList.isNotEmpty() ||
|
||||
hasSticker() ||
|
||||
hasReaction() ||
|
||||
return attachments.isNotEmpty() ||
|
||||
body != null ||
|
||||
quote != null ||
|
||||
contact.isNotEmpty() ||
|
||||
preview.isNotEmpty() ||
|
||||
bodyRanges.isNotEmpty() ||
|
||||
sticker != null ||
|
||||
reaction != null ||
|
||||
hasRemoteDelete
|
||||
}
|
||||
|
||||
val DataMessage.hasDisallowedAnnouncementOnlyContent: Boolean
|
||||
get() {
|
||||
return hasBody() ||
|
||||
attachmentsList.isNotEmpty() ||
|
||||
hasQuote() ||
|
||||
previewList.isNotEmpty() ||
|
||||
bodyRangesList.isNotEmpty() ||
|
||||
hasSticker()
|
||||
return body != null ||
|
||||
attachments.isNotEmpty() ||
|
||||
quote != null ||
|
||||
preview.isNotEmpty() ||
|
||||
bodyRanges.isNotEmpty() ||
|
||||
sticker != null
|
||||
}
|
||||
|
||||
val DataMessage.isExpirationUpdate: Boolean
|
||||
get() = flags and DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE != 0
|
||||
get() = flags != null && flags!! and DataMessage.Flags.EXPIRATION_TIMER_UPDATE.value != 0
|
||||
|
||||
val DataMessage.hasRemoteDelete: Boolean
|
||||
get() = hasDelete() && delete.hasTargetSentTimestamp()
|
||||
get() = delete != null && delete!!.targetSentTimestamp != null
|
||||
|
||||
val DataMessage.isGroupV2Update: Boolean
|
||||
get() = !hasRenderableContent && hasSignedGroupChange
|
||||
|
||||
val DataMessage.hasGroupContext: Boolean
|
||||
get() = hasGroupV2() && groupV2.hasMasterKey() && groupV2.masterKey.isNotEmpty
|
||||
val DataMessage?.hasGroupContext: Boolean
|
||||
get() = this?.groupV2?.masterKey.isNotEmpty()
|
||||
|
||||
val DataMessage.hasSignedGroupChange: Boolean
|
||||
get() = hasGroupContext && groupV2.hasSignedGroupChange
|
||||
get() = hasGroupContext && groupV2!!.hasSignedGroupChange
|
||||
|
||||
val DataMessage.isMediaMessage: Boolean
|
||||
get() = attachmentsList.isNotEmpty() || hasQuote() || contactList.isNotEmpty() || hasSticker() || bodyRangesList.isNotEmpty() || previewList.isNotEmpty()
|
||||
get() = attachments.isNotEmpty() || quote != null || contact.isNotEmpty() || sticker != null || bodyRanges.isNotEmpty() || preview.isNotEmpty()
|
||||
|
||||
val DataMessage.isEndSession: Boolean
|
||||
get() = flags and DataMessage.Flags.END_SESSION_VALUE != 0
|
||||
get() = flags != null && flags!! and DataMessage.Flags.END_SESSION.value != 0
|
||||
|
||||
val DataMessage.isStoryReaction: Boolean
|
||||
get() = hasReaction() && hasStoryContext()
|
||||
get() = reaction != null && storyContext != null
|
||||
|
||||
val DataMessage.isPaymentActivationRequest: Boolean
|
||||
get() = hasPayment() && payment.hasActivation() && payment.activation.type == Payment.Activation.Type.REQUEST
|
||||
get() = payment?.activation?.type == Payment.Activation.Type.REQUEST
|
||||
|
||||
val DataMessage.isPaymentActivated: Boolean
|
||||
get() = hasPayment() && payment.hasActivation() && payment.activation.type == Payment.Activation.Type.ACTIVATED
|
||||
get() = payment?.activation?.type == Payment.Activation.Type.ACTIVATED
|
||||
|
||||
val DataMessage.isInvalid: Boolean
|
||||
get() {
|
||||
if (isViewOnce) {
|
||||
val contentType = attachmentsList[0].contentType.lowercase()
|
||||
return attachmentsList.size != 1 || !MediaUtil.isImageOrVideoType(contentType)
|
||||
if (isViewOnce == true) {
|
||||
val contentType = attachments[0].contentType?.lowercase()
|
||||
return attachments.size != 1 || !MediaUtil.isImageOrVideoType(contentType)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -98,31 +98,34 @@ object SignalServiceProtoUtil {
|
||||
val DataMessage.isEmptyGroupV2Message: Boolean
|
||||
get() = hasGroupContext && !isGroupV2Update && !hasRenderableContent
|
||||
|
||||
val DataMessage.expireTimerDuration: Duration
|
||||
get() = (expireTimer ?: 0).seconds
|
||||
|
||||
val GroupContextV2.hasSignedGroupChange: Boolean
|
||||
get() = hasGroupChange() && groupChange.isNotEmpty
|
||||
get() = groupChange.isNotEmpty()
|
||||
|
||||
val GroupContextV2.signedGroupChange: ByteArray
|
||||
get() = groupChange.toByteArray()
|
||||
get() = groupChange!!.toByteArray()
|
||||
|
||||
val GroupContextV2.groupMasterKey: GroupMasterKey
|
||||
get() = GroupMasterKey(masterKey.toByteArray())
|
||||
get() = GroupMasterKey(masterKey!!.toByteArray())
|
||||
|
||||
val GroupContextV2?.isValid: Boolean
|
||||
get() = this != null && masterKey.isNotEmpty
|
||||
get() = this?.masterKey.isNotEmpty()
|
||||
|
||||
val GroupContextV2.groupId: GroupId.V2?
|
||||
get() = if (isValid) GroupId.v2(groupMasterKey) else null
|
||||
|
||||
val StoryMessage.type: StoryType
|
||||
get() {
|
||||
return if (allowsReplies) {
|
||||
if (hasTextAttachment()) {
|
||||
return if (allowsReplies == true) {
|
||||
if (textAttachment != null) {
|
||||
StoryType.TEXT_STORY_WITH_REPLIES
|
||||
} else {
|
||||
StoryType.STORY_WITH_REPLIES
|
||||
}
|
||||
} else {
|
||||
if (hasTextAttachment()) {
|
||||
if (textAttachment != null) {
|
||||
StoryType.TEXT_STORY_WITHOUT_REPLIES
|
||||
} else {
|
||||
StoryType.STORY_WITHOUT_REPLIES
|
||||
@@ -131,16 +134,16 @@ object SignalServiceProtoUtil {
|
||||
}
|
||||
|
||||
fun Sent.isUnidentified(serviceId: ServiceId?): Boolean {
|
||||
return serviceId != null && unidentifiedStatusList.firstOrNull { ServiceId.parseOrNull(it.destinationServiceId) == serviceId }?.unidentified ?: false
|
||||
return serviceId != null && unidentifiedStatus.firstOrNull { ServiceId.parseOrNull(it.destinationServiceId) == serviceId }?.unidentified ?: false
|
||||
}
|
||||
|
||||
val Sent.serviceIdsToUnidentifiedStatus: Map<ServiceId, Boolean>
|
||||
get() {
|
||||
return unidentifiedStatusList
|
||||
return unidentifiedStatus
|
||||
.mapNotNull { status ->
|
||||
val serviceId = ServiceId.parseOrNull(status.destinationServiceId)
|
||||
if (serviceId != null) {
|
||||
serviceId to status.unidentified
|
||||
serviceId to (status.unidentified ?: false)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -149,7 +152,7 @@ object SignalServiceProtoUtil {
|
||||
}
|
||||
|
||||
val TypingMessage.hasStarted: Boolean
|
||||
get() = hasAction() && action == TypingMessage.Action.STARTED
|
||||
get() = action == TypingMessage.Action.STARTED
|
||||
|
||||
fun ByteString.toDecryptionErrorMessage(metadata: EnvelopeMetadata): DecryptionErrorMessage {
|
||||
try {
|
||||
@@ -180,7 +183,7 @@ object SignalServiceProtoUtil {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <reified MessageType : GeneratedMessageLite<MessageType, BuilderType>, BuilderType : GeneratedMessageLite.Builder<MessageType, BuilderType>> GeneratedMessageLite.Builder<MessageType, BuilderType>.buildWith(block: BuilderType.() -> Unit): MessageType {
|
||||
inline fun <reified MessageType : Message<MessageType, BuilderType>, BuilderType : Message.Builder<MessageType, BuilderType>> Message.Builder<MessageType, BuilderType>.buildWith(block: BuilderType.() -> Unit): MessageType {
|
||||
block(this as BuilderType)
|
||||
return build()
|
||||
}
|
||||
|
||||
@@ -20,28 +20,30 @@ import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.TextAttachment
|
||||
|
||||
object StoryMessageProcessor {
|
||||
|
||||
fun process(envelope: Envelope, content: SignalServiceProtos.Content, metadata: EnvelopeMetadata, senderRecipient: Recipient, threadRecipient: Recipient) {
|
||||
val storyMessage = content.storyMessage
|
||||
fun process(envelope: Envelope, content: Content, metadata: EnvelopeMetadata, senderRecipient: Recipient, threadRecipient: Recipient) {
|
||||
val storyMessage = content.storyMessage!!
|
||||
|
||||
log(envelope.timestamp, "Story message.")
|
||||
log(envelope.timestamp!!, "Story message.")
|
||||
|
||||
if (threadRecipient.isInactiveGroup) {
|
||||
warn(envelope.timestamp, "Dropping a group story from a group we're no longer in.")
|
||||
warn(envelope.timestamp!!, "Dropping a group story from a group we're no longer in.")
|
||||
return
|
||||
}
|
||||
|
||||
if (threadRecipient.isGroup && !SignalDatabase.groups.isCurrentMember(threadRecipient.requireGroupId().requirePush(), senderRecipient.id)) {
|
||||
warn(envelope.timestamp, "Dropping a group story from a user who's no longer a member.")
|
||||
warn(envelope.timestamp!!, "Dropping a group story from a user who's no longer a member.")
|
||||
return
|
||||
}
|
||||
|
||||
if (!threadRecipient.isGroup && !(senderRecipient.isProfileSharing || senderRecipient.isSystemContact)) {
|
||||
warn(envelope.timestamp, "Dropping story from an untrusted source.")
|
||||
warn(envelope.timestamp!!, "Dropping story from an untrusted source.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -50,29 +52,29 @@ object StoryMessageProcessor {
|
||||
SignalDatabase.messages.beginTransaction()
|
||||
|
||||
try {
|
||||
val storyType: StoryType = if (storyMessage.hasAllowsReplies() && storyMessage.allowsReplies) {
|
||||
StoryType.withReplies(storyMessage.hasTextAttachment())
|
||||
val storyType: StoryType = if (storyMessage.allowsReplies == true) {
|
||||
StoryType.withReplies(isTextStory = storyMessage.textAttachment != null)
|
||||
} else {
|
||||
StoryType.withoutReplies(storyMessage.hasTextAttachment())
|
||||
StoryType.withoutReplies(isTextStory = storyMessage.textAttachment != null)
|
||||
}
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
from = senderRecipient.id,
|
||||
sentTimeMillis = envelope.timestamp,
|
||||
serverTimeMillis = envelope.serverTimestamp,
|
||||
sentTimeMillis = envelope.timestamp!!,
|
||||
serverTimeMillis = envelope.serverTimestamp!!,
|
||||
receivedTimeMillis = System.currentTimeMillis(),
|
||||
storyType = storyType,
|
||||
isUnidentified = metadata.sealedSender,
|
||||
body = serializeTextAttachment(storyMessage),
|
||||
groupId = storyMessage.group.groupId,
|
||||
attachments = if (storyMessage.hasFileAttachment()) listOfNotNull(storyMessage.fileAttachment.toPointer()) else emptyList(),
|
||||
groupId = storyMessage.group?.groupId,
|
||||
attachments = listOfNotNull(storyMessage.fileAttachment?.toPointer()),
|
||||
linkPreviews = DataMessageProcessor.getLinkPreviews(
|
||||
previews = if (storyMessage.textAttachment.hasPreview()) listOf(storyMessage.textAttachment.preview) else emptyList(),
|
||||
previews = listOfNotNull(storyMessage.textAttachment?.preview),
|
||||
body = "",
|
||||
isStoryEmbed = true
|
||||
),
|
||||
serverGuid = envelope.serverGuid,
|
||||
messageRanges = storyMessage.bodyRangesList.filterNot { it.hasMentionAci() }.toBodyRangeList()
|
||||
messageRanges = storyMessage.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
|
||||
)
|
||||
|
||||
insertResult = SignalDatabase.messages.insertSecureDecryptedMessageInbox(mediaMessage, -1).orNull()
|
||||
@@ -91,66 +93,60 @@ object StoryMessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fun serializeTextAttachment(story: SignalServiceProtos.StoryMessage): String? {
|
||||
if (!story.hasTextAttachment()) {
|
||||
return null
|
||||
}
|
||||
val textAttachment = story.textAttachment
|
||||
val builder = StoryTextPost.newBuilder()
|
||||
fun serializeTextAttachment(story: StoryMessage): String? {
|
||||
val textAttachment = story.textAttachment ?: return null
|
||||
val builder = StoryTextPost.Builder()
|
||||
|
||||
if (textAttachment.hasText()) {
|
||||
builder.body = textAttachment.text
|
||||
if (textAttachment.text != null) {
|
||||
builder.body = textAttachment.text!!
|
||||
}
|
||||
|
||||
if (textAttachment.hasTextStyle()) {
|
||||
when (textAttachment.textStyle) {
|
||||
SignalServiceProtos.TextAttachment.Style.DEFAULT -> builder.style = StoryTextPost.Style.DEFAULT
|
||||
SignalServiceProtos.TextAttachment.Style.REGULAR -> builder.style = StoryTextPost.Style.REGULAR
|
||||
SignalServiceProtos.TextAttachment.Style.BOLD -> builder.style = StoryTextPost.Style.BOLD
|
||||
SignalServiceProtos.TextAttachment.Style.SERIF -> builder.style = StoryTextPost.Style.SERIF
|
||||
SignalServiceProtos.TextAttachment.Style.SCRIPT -> builder.style = StoryTextPost.Style.SCRIPT
|
||||
SignalServiceProtos.TextAttachment.Style.CONDENSED -> builder.style = StoryTextPost.Style.CONDENSED
|
||||
else -> Unit
|
||||
}
|
||||
when (textAttachment.textStyle) {
|
||||
TextAttachment.Style.DEFAULT -> builder.style = StoryTextPost.Style.DEFAULT
|
||||
TextAttachment.Style.REGULAR -> builder.style = StoryTextPost.Style.REGULAR
|
||||
TextAttachment.Style.BOLD -> builder.style = StoryTextPost.Style.BOLD
|
||||
TextAttachment.Style.SERIF -> builder.style = StoryTextPost.Style.SERIF
|
||||
TextAttachment.Style.SCRIPT -> builder.style = StoryTextPost.Style.SCRIPT
|
||||
TextAttachment.Style.CONDENSED -> builder.style = StoryTextPost.Style.CONDENSED
|
||||
null -> Unit
|
||||
}
|
||||
|
||||
if (textAttachment.hasTextBackgroundColor()) {
|
||||
builder.textBackgroundColor = textAttachment.textBackgroundColor
|
||||
if (textAttachment.textBackgroundColor != null) {
|
||||
builder.textBackgroundColor = textAttachment.textBackgroundColor!!
|
||||
}
|
||||
|
||||
if (textAttachment.hasTextForegroundColor()) {
|
||||
builder.textForegroundColor = textAttachment.textForegroundColor
|
||||
if (textAttachment.textForegroundColor != null) {
|
||||
builder.textForegroundColor = textAttachment.textForegroundColor!!
|
||||
}
|
||||
|
||||
val chatColorBuilder = ChatColor.newBuilder()
|
||||
val chatColorBuilder = ChatColor.Builder()
|
||||
|
||||
if (textAttachment.hasColor()) {
|
||||
chatColorBuilder.setSingleColor(ChatColor.SingleColor.newBuilder().setColor(textAttachment.color))
|
||||
} else if (textAttachment.hasGradient()) {
|
||||
val gradient = textAttachment.gradient
|
||||
val linearGradientBuilder = ChatColor.LinearGradient.newBuilder()
|
||||
linearGradientBuilder.rotation = gradient.angle.toFloat()
|
||||
if (textAttachment.color != null) {
|
||||
chatColorBuilder.singleColor(ChatColor.SingleColor.Builder().color(textAttachment.color!!).build())
|
||||
} else if (textAttachment.gradient != null) {
|
||||
val gradient = textAttachment.gradient!!
|
||||
val linearGradientBuilder = ChatColor.LinearGradient.Builder()
|
||||
linearGradientBuilder.rotation = (gradient.angle ?: 0).toFloat()
|
||||
|
||||
if (gradient.positionsList.size > 1 && gradient.colorsList.size == gradient.positionsList.size) {
|
||||
val positions = ArrayList(gradient.positionsList)
|
||||
if (gradient.positions.size > 1 && gradient.colors.size == gradient.positions.size) {
|
||||
val positions = ArrayList(gradient.positions)
|
||||
positions[0] = 0f
|
||||
positions[positions.size - 1] = 1f
|
||||
linearGradientBuilder.addAllColors(ArrayList(gradient.colorsList))
|
||||
linearGradientBuilder.addAllPositions(positions)
|
||||
} else if (gradient.colorsList.isNotEmpty()) {
|
||||
linearGradientBuilder.colors(ArrayList(gradient.colors))
|
||||
linearGradientBuilder.positions(positions)
|
||||
} else if (gradient.colors.isNotEmpty()) {
|
||||
warn("Incoming text story has color / position mismatch. Defaulting to start and end colors.")
|
||||
linearGradientBuilder.addColors(gradient.colorsList[0])
|
||||
linearGradientBuilder.addColors(gradient.colorsList[gradient.colorsList.size - 1])
|
||||
linearGradientBuilder.addAllPositions(listOf(0f, 1f))
|
||||
linearGradientBuilder.colors(listOf(gradient.colors[0], gradient.colors[gradient.colors.size - 1]))
|
||||
linearGradientBuilder.positions(listOf(0f, 1f))
|
||||
} else {
|
||||
warn("Incoming text story did not have a valid linear gradient.")
|
||||
linearGradientBuilder.addAllColors(listOf(Color.BLACK, Color.BLACK))
|
||||
linearGradientBuilder.addAllPositions(listOf(0f, 1f))
|
||||
linearGradientBuilder.colors(listOf(Color.BLACK, Color.BLACK))
|
||||
linearGradientBuilder.positions(listOf(0f, 1f))
|
||||
}
|
||||
chatColorBuilder.setLinearGradient(linearGradientBuilder)
|
||||
chatColorBuilder.linearGradient(linearGradientBuilder.build())
|
||||
}
|
||||
builder.setBackground(chatColorBuilder)
|
||||
builder.background(chatColorBuilder.build())
|
||||
|
||||
return Base64.encodeBytes(builder.build().toByteArray())
|
||||
return Base64.encodeBytes(builder.build().encode())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceTextAttachment
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object StorySendUtil {
|
||||
@JvmStatic
|
||||
@Throws(InvalidProtocolBufferException::class)
|
||||
@Throws(IOException::class)
|
||||
fun deserializeBodyToStoryTextAttachment(message: OutgoingMessage, getPreviewsFor: (OutgoingMessage) -> List<SignalServicePreview>): SignalServiceTextAttachment {
|
||||
val storyTextPost = StoryTextPost.parseFrom(Base64.decode(message.body))
|
||||
val storyTextPost = StoryTextPost.ADAPTER.decode(Base64.decode(message.body))
|
||||
val preview = if (message.linkPreviews.isEmpty()) {
|
||||
Optional.empty()
|
||||
} else {
|
||||
Optional.of(getPreviewsFor(message)[0])
|
||||
}
|
||||
|
||||
return if (storyTextPost.background.hasLinearGradient()) {
|
||||
return if (storyTextPost.background!!.linearGradient != null) {
|
||||
SignalServiceTextAttachment.forGradientBackground(
|
||||
Optional.ofNullable(storyTextPost.body),
|
||||
Optional.ofNullable(getStyle(storyTextPost.style)),
|
||||
@@ -28,9 +28,9 @@ object StorySendUtil {
|
||||
Optional.of(storyTextPost.textBackgroundColor),
|
||||
preview,
|
||||
SignalServiceTextAttachment.Gradient(
|
||||
Optional.of(storyTextPost.background.linearGradient.rotation.roundToInt()),
|
||||
ArrayList(storyTextPost.background.linearGradient.colorsList),
|
||||
ArrayList(storyTextPost.background.linearGradient.positionsList)
|
||||
Optional.of(storyTextPost.background.linearGradient!!.rotation.roundToInt()),
|
||||
ArrayList(storyTextPost.background.linearGradient.colors),
|
||||
ArrayList(storyTextPost.background.linearGradient.positions)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@@ -40,7 +40,7 @@ object StorySendUtil {
|
||||
Optional.of(storyTextPost.textForegroundColor),
|
||||
Optional.of(storyTextPost.textBackgroundColor),
|
||||
preview,
|
||||
storyTextPost.background.singleColor.color
|
||||
storyTextPost.background.singleColor!!.color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import ProtoUtil.isNotEmpty
|
||||
import android.content.Context
|
||||
import com.google.protobuf.ByteString
|
||||
import com.mobilecoin.lib.exceptions.SerializationException
|
||||
import okio.ByteString
|
||||
import org.signal.core.util.Hex
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.libsignal.protocol.util.Pair
|
||||
@@ -54,6 +55,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.warn
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.expireTimerDuration
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.groupId
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.groupMasterKey
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.hasGroupContext
|
||||
@@ -100,27 +102,28 @@ import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.api.storage.StorageKey
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLogEvent
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.FetchLatest
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.MessageRequestResponse
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ViewOnceOpen
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.EditMessage
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Blocked
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.CallLinkUpdate
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.CallLogEvent
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Configuration
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.FetchLatest
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.MessageRequestResponse
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Read
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Request
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.Sent
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.StickerPackOperation
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage.ViewOnceOpen
|
||||
import org.whispersystems.signalservice.internal.push.Verified
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.Duration
|
||||
|
||||
object SyncMessageProcessor {
|
||||
|
||||
@@ -132,27 +135,27 @@ object SyncMessageProcessor {
|
||||
metadata: EnvelopeMetadata,
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
) {
|
||||
val syncMessage = content.syncMessage
|
||||
val syncMessage = content.syncMessage!!
|
||||
|
||||
when {
|
||||
syncMessage.hasSent() -> handleSynchronizeSentMessage(context, envelope, content, metadata, syncMessage.sent, senderRecipient, earlyMessageCacheEntry)
|
||||
syncMessage.hasRequest() -> handleSynchronizeRequestMessage(context, syncMessage.request, envelope.timestamp)
|
||||
syncMessage.readList.isNotEmpty() -> handleSynchronizeReadMessage(context, syncMessage.readList, envelope.timestamp, earlyMessageCacheEntry)
|
||||
syncMessage.viewedList.isNotEmpty() -> handleSynchronizeViewedMessage(context, syncMessage.viewedList, envelope.timestamp)
|
||||
syncMessage.hasViewOnceOpen() -> handleSynchronizeViewOnceOpenMessage(context, syncMessage.viewOnceOpen, envelope.timestamp, earlyMessageCacheEntry)
|
||||
syncMessage.hasVerified() -> handleSynchronizeVerifiedMessage(context, syncMessage.verified)
|
||||
syncMessage.stickerPackOperationList.isNotEmpty() -> handleSynchronizeStickerPackOperation(syncMessage.stickerPackOperationList, envelope.timestamp)
|
||||
syncMessage.hasConfiguration() -> handleSynchronizeConfigurationMessage(context, syncMessage.configuration, envelope.timestamp)
|
||||
syncMessage.hasBlocked() -> handleSynchronizeBlockedListMessage(syncMessage.blocked)
|
||||
syncMessage.hasFetchLatest() && syncMessage.fetchLatest.hasType() -> handleSynchronizeFetchMessage(syncMessage.fetchLatest.type, envelope.timestamp)
|
||||
syncMessage.hasMessageRequestResponse() -> handleSynchronizeMessageRequestResponse(syncMessage.messageRequestResponse, envelope.timestamp)
|
||||
syncMessage.hasOutgoingPayment() -> handleSynchronizeOutgoingPayment(syncMessage.outgoingPayment, envelope.timestamp)
|
||||
syncMessage.hasKeys() && syncMessage.keys.hasStorageService() -> handleSynchronizeKeys(syncMessage.keys.storageService, envelope.timestamp)
|
||||
syncMessage.hasContacts() -> handleSynchronizeContacts(syncMessage.contacts, envelope.timestamp)
|
||||
syncMessage.hasCallEvent() -> handleSynchronizeCallEvent(syncMessage.callEvent, envelope.timestamp)
|
||||
syncMessage.hasCallLinkUpdate() -> handleSynchronizeCallLink(syncMessage.callLinkUpdate, envelope.timestamp)
|
||||
syncMessage.hasCallLogEvent() -> handleSynchronizeCallLogEvent(syncMessage.callLogEvent, envelope.timestamp)
|
||||
else -> warn(envelope.timestamp, "Contains no known sync types...")
|
||||
syncMessage.sent != null -> handleSynchronizeSentMessage(context, envelope, content, metadata, syncMessage.sent!!, senderRecipient, earlyMessageCacheEntry)
|
||||
syncMessage.request != null -> handleSynchronizeRequestMessage(context, syncMessage.request!!, envelope.timestamp!!)
|
||||
syncMessage.read.isNotEmpty() -> handleSynchronizeReadMessage(context, syncMessage.read, envelope.timestamp!!, earlyMessageCacheEntry)
|
||||
syncMessage.viewed.isNotEmpty() -> handleSynchronizeViewedMessage(context, syncMessage.viewed, envelope.timestamp!!)
|
||||
syncMessage.viewOnceOpen != null -> handleSynchronizeViewOnceOpenMessage(context, syncMessage.viewOnceOpen!!, envelope.timestamp!!, earlyMessageCacheEntry)
|
||||
syncMessage.verified != null -> handleSynchronizeVerifiedMessage(context, syncMessage.verified!!)
|
||||
syncMessage.stickerPackOperation.isNotEmpty() -> handleSynchronizeStickerPackOperation(syncMessage.stickerPackOperation, envelope.timestamp!!)
|
||||
syncMessage.configuration != null -> handleSynchronizeConfigurationMessage(context, syncMessage.configuration!!, envelope.timestamp!!)
|
||||
syncMessage.blocked != null -> handleSynchronizeBlockedListMessage(syncMessage.blocked!!)
|
||||
syncMessage.fetchLatest?.type != null -> handleSynchronizeFetchMessage(syncMessage.fetchLatest!!.type!!, envelope.timestamp!!)
|
||||
syncMessage.messageRequestResponse != null -> handleSynchronizeMessageRequestResponse(syncMessage.messageRequestResponse!!, envelope.timestamp!!)
|
||||
syncMessage.outgoingPayment != null -> handleSynchronizeOutgoingPayment(syncMessage.outgoingPayment!!, envelope.timestamp!!)
|
||||
syncMessage.keys?.storageService != null -> handleSynchronizeKeys(syncMessage.keys!!.storageService!!, envelope.timestamp!!)
|
||||
syncMessage.contacts != null -> handleSynchronizeContacts(syncMessage.contacts!!, envelope.timestamp!!)
|
||||
syncMessage.callEvent != null -> handleSynchronizeCallEvent(syncMessage.callEvent!!, envelope.timestamp!!)
|
||||
syncMessage.callLinkUpdate != null -> handleSynchronizeCallLink(syncMessage.callLinkUpdate!!, envelope.timestamp!!)
|
||||
syncMessage.callLogEvent != null -> handleSynchronizeCallLogEvent(syncMessage.callLogEvent!!, envelope.timestamp!!)
|
||||
else -> warn(envelope.timestamp!!, "Contains no known sync types...")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,54 +169,64 @@ object SyncMessageProcessor {
|
||||
senderRecipient: Recipient,
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
) {
|
||||
log(envelope.timestamp, "Processing sent transcript for message with ID ${sent.timestamp}")
|
||||
log(envelope.timestamp!!, "Processing sent transcript for message with ID ${sent.timestamp!!}")
|
||||
|
||||
try {
|
||||
if (sent.hasStoryMessage() || sent.storyMessageRecipientsList.isNotEmpty()) {
|
||||
if (sent.storyMessage != null || sent.storyMessageRecipients.isNotEmpty()) {
|
||||
handleSynchronizeSentStoryMessage(envelope, sent)
|
||||
return
|
||||
}
|
||||
|
||||
if (sent.hasEditMessage()) {
|
||||
if (sent.editMessage != null) {
|
||||
handleSynchronizeSentEditMessage(context, envelope, sent, senderRecipient, earlyMessageCacheEntry)
|
||||
return
|
||||
}
|
||||
|
||||
val dataMessage = sent.message
|
||||
val groupId: GroupId.V2? = if (dataMessage.hasGroupContext) GroupId.v2(dataMessage.groupV2.groupMasterKey) else null
|
||||
if (sent.isRecipientUpdate == true) {
|
||||
handleGroupRecipientUpdate(sent, envelope.timestamp!!)
|
||||
return
|
||||
}
|
||||
|
||||
val dataMessage = if (sent.message != null) {
|
||||
sent.message!!
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "Sync message missing nested message to sync")
|
||||
return
|
||||
}
|
||||
|
||||
val groupId: GroupId.V2? = if (dataMessage.hasGroupContext) GroupId.v2(dataMessage.groupV2!!.groupMasterKey) else null
|
||||
|
||||
if (groupId != null) {
|
||||
if (MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp, content, metadata, groupId, dataMessage.groupV2, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
|
||||
if (MessageContentProcessor.handleGv2PreProcessing(context, envelope.timestamp!!, content, metadata, groupId, dataMessage.groupV2!!, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var threadId: Long = -1
|
||||
when {
|
||||
sent.isRecipientUpdate -> handleGroupRecipientUpdate(sent, envelope.timestamp)
|
||||
dataMessage.isEndSession -> threadId = handleSynchronizeSentEndSessionMessage(context, sent, envelope.timestamp)
|
||||
dataMessage.isEndSession -> threadId = handleSynchronizeSentEndSessionMessage(context, sent, envelope.timestamp!!)
|
||||
dataMessage.isGroupV2Update -> {
|
||||
handleSynchronizeSentGv2Update(context, envelope, sent)
|
||||
threadId = SignalDatabase.threads.getOrCreateThreadIdFor(getSyncMessageDestination(sent))
|
||||
}
|
||||
dataMessage.hasGroupCallUpdate() -> DataMessageProcessor.handleGroupCallUpdateMessage(envelope, dataMessage, senderRecipient.id, groupId)
|
||||
dataMessage.isEmptyGroupV2Message -> warn(envelope.timestamp, "Empty GV2 message! Doing nothing.")
|
||||
dataMessage.groupCallUpdate != null -> DataMessageProcessor.handleGroupCallUpdateMessage(envelope, dataMessage, senderRecipient.id, groupId)
|
||||
dataMessage.isEmptyGroupV2Message -> warn(envelope.timestamp!!, "Empty GV2 message! Doing nothing.")
|
||||
dataMessage.isExpirationUpdate -> threadId = handleSynchronizeSentExpirationUpdate(sent)
|
||||
dataMessage.hasStoryContext() -> threadId = handleSynchronizeSentStoryReply(sent, envelope.timestamp)
|
||||
dataMessage.hasReaction() -> {
|
||||
dataMessage.storyContext != null -> threadId = handleSynchronizeSentStoryReply(sent, envelope.timestamp!!)
|
||||
dataMessage.reaction != null -> {
|
||||
DataMessageProcessor.handleReaction(context, envelope, dataMessage, senderRecipient.id, earlyMessageCacheEntry)
|
||||
threadId = SignalDatabase.threads.getOrCreateThreadIdFor(getSyncMessageDestination(sent))
|
||||
}
|
||||
dataMessage.hasRemoteDelete -> DataMessageProcessor.handleRemoteDelete(context, envelope, dataMessage, senderRecipient.id, earlyMessageCacheEntry)
|
||||
dataMessage.isMediaMessage -> threadId = handleSynchronizeSentMediaMessage(context, sent, envelope.timestamp)
|
||||
else -> threadId = handleSynchronizeSentTextMessage(sent, envelope.timestamp)
|
||||
dataMessage.isMediaMessage -> threadId = handleSynchronizeSentMediaMessage(context, sent, envelope.timestamp!!)
|
||||
else -> threadId = handleSynchronizeSentTextMessage(sent, envelope.timestamp!!)
|
||||
}
|
||||
|
||||
if (groupId != null && SignalDatabase.groups.isUnknownGroup(groupId)) {
|
||||
DataMessageProcessor.handleUnknownGroupMessage(envelope.timestamp, dataMessage.groupV2)
|
||||
DataMessageProcessor.handleUnknownGroupMessage(envelope.timestamp!!, dataMessage.groupV2!!)
|
||||
}
|
||||
|
||||
if (dataMessage.hasProfileKey()) {
|
||||
if (dataMessage.profileKey.isNotEmpty()) {
|
||||
val recipient: Recipient = getSyncMessageDestination(sent)
|
||||
if (!recipient.isSystemContact && !recipient.isProfileSharing) {
|
||||
SignalDatabase.recipients.setProfileSharing(recipient.id, true)
|
||||
@@ -226,11 +239,11 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
if (SignalStore.rateLimit().needsRecaptcha()) {
|
||||
log(envelope.timestamp, "Got a sent transcript while in reCAPTCHA mode. Assuming we're good to message again.")
|
||||
log(envelope.timestamp!!, "Got a sent transcript while in reCAPTCHA mode. Assuming we're good to message again.")
|
||||
RateLimitUtil.retryAllRateLimitedMessages(context)
|
||||
}
|
||||
|
||||
ApplicationDependencies.getMessageNotifier().setLastDesktopActivityTimestamp(sent.timestamp)
|
||||
ApplicationDependencies.getMessageNotifier().setLastDesktopActivityTimestamp(sent.timestamp!!)
|
||||
} catch (e: MmsException) {
|
||||
throw StorageFailedException(e, metadata.sourceServiceId.toString(), metadata.sourceDeviceId)
|
||||
}
|
||||
@@ -238,9 +251,9 @@ object SyncMessageProcessor {
|
||||
|
||||
private fun getSyncMessageDestination(message: Sent): Recipient {
|
||||
return if (message.message.hasGroupContext) {
|
||||
Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.message.groupV2.groupMasterKey))
|
||||
Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.message!!.groupV2!!.groupMasterKey))
|
||||
} else {
|
||||
Recipient.externalPush(SignalServiceAddress(ServiceId.parseOrThrow(message.destinationServiceId), message.destinationE164))
|
||||
Recipient.externalPush(SignalServiceAddress(ServiceId.parseOrThrow(message.destinationServiceId!!), message.destinationE164))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,30 +265,32 @@ object SyncMessageProcessor {
|
||||
senderRecipient: Recipient,
|
||||
earlyMessageCacheEntry: EarlyMessageCacheEntry?
|
||||
) {
|
||||
val targetSentTimestamp: Long = sent.editMessage.targetSentTimestamp
|
||||
val editMessage: EditMessage = sent.editMessage!!
|
||||
val targetSentTimestamp: Long = editMessage.targetSentTimestamp!!
|
||||
val targetMessage: MessageRecord? = SignalDatabase.messages.getMessageFor(targetSentTimestamp, senderRecipient.id)
|
||||
val senderRecipientId = senderRecipient.id
|
||||
|
||||
if (targetMessage == null) {
|
||||
warn(envelope.timestamp, "[handleSynchronizeSentEditMessage] Could not find matching message! targetTimestamp: $targetSentTimestamp author: $senderRecipientId")
|
||||
warn(envelope.timestamp!!, "[handleSynchronizeSentEditMessage] Could not find matching message! targetTimestamp: $targetSentTimestamp author: $senderRecipientId")
|
||||
if (earlyMessageCacheEntry != null) {
|
||||
ApplicationDependencies.getEarlyMessageCache().store(senderRecipientId, targetSentTimestamp, earlyMessageCacheEntry)
|
||||
PushProcessEarlyMessagesJob.enqueue()
|
||||
}
|
||||
} else if (MessageConstraintsUtil.isValidEditMessageReceive(targetMessage, senderRecipient, envelope.serverTimestamp)) {
|
||||
val message = sent.editMessage.dataMessage
|
||||
} else if (MessageConstraintsUtil.isValidEditMessageReceive(targetMessage, senderRecipient, envelope.serverTimestamp!!)) {
|
||||
val message: DataMessage = editMessage.dataMessage!!
|
||||
val toRecipient: Recipient = if (message.hasGroupContext) {
|
||||
Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.groupV2.groupMasterKey))
|
||||
Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.groupV2!!.groupMasterKey))
|
||||
} else {
|
||||
Recipient.externalPush(ServiceId.parseOrThrow(sent.destinationServiceId))
|
||||
Recipient.externalPush(ServiceId.parseOrThrow(sent.destinationServiceId!!))
|
||||
}
|
||||
|
||||
if (message.isMediaMessage) {
|
||||
handleSynchronizeSentEditMediaMessage(context, targetMessage, toRecipient, sent, message, envelope.timestamp)
|
||||
handleSynchronizeSentEditMediaMessage(context, targetMessage, toRecipient, sent, message, envelope.timestamp!!)
|
||||
} else {
|
||||
handleSynchronizeSentEditTextMessage(targetMessage, toRecipient, sent, message, envelope.timestamp)
|
||||
handleSynchronizeSentEditTextMessage(targetMessage, toRecipient, sent, message, envelope.timestamp!!)
|
||||
}
|
||||
} else {
|
||||
warn(envelope.timestamp, "[handleSynchronizeSentEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${targetMessage.serverTimestamp}, sendAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
|
||||
warn(envelope.timestamp!!, "[handleSynchronizeSentEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${targetMessage.serverTimestamp}, sendAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +304,7 @@ object SyncMessageProcessor {
|
||||
log(envelopeTimestamp, "Synchronize sent edit text message for message: ${targetMessage.id}")
|
||||
|
||||
val body = message.body ?: ""
|
||||
val bodyRanges = message.bodyRangesList.filterNot { it.hasMentionAci() }.toBodyRangeList()
|
||||
val bodyRanges = message.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
|
||||
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(toRecipient)
|
||||
val isGroup = toRecipient.isGroup
|
||||
@@ -299,7 +314,7 @@ object SyncMessageProcessor {
|
||||
val outgoingMessage = OutgoingMessage(
|
||||
recipient = toRecipient,
|
||||
body = body,
|
||||
timestamp = sent.timestamp,
|
||||
timestamp = sent.timestamp!!,
|
||||
expiresIn = targetMessage.expiresIn,
|
||||
isSecure = true,
|
||||
bodyRanges = bodyRanges,
|
||||
@@ -311,7 +326,7 @@ object SyncMessageProcessor {
|
||||
} else {
|
||||
val outgoingTextMessage = OutgoingMessage(
|
||||
threadRecipient = toRecipient,
|
||||
sentTimeMillis = sent.timestamp,
|
||||
sentTimeMillis = sent.timestamp!!,
|
||||
body = body,
|
||||
expiresIn = targetMessage.expiresIn,
|
||||
isUrgent = true,
|
||||
@@ -330,8 +345,8 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
if (toRecipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, toRecipient.id, System.currentTimeMillis())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,12 +362,12 @@ object SyncMessageProcessor {
|
||||
|
||||
val quote: QuoteModel? = DataMessageProcessor.getValidatedQuote(context, envelopeTimestamp, message)
|
||||
val sharedContacts: List<Contact> = DataMessageProcessor.getContacts(message)
|
||||
val previews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(message.previewList, message.body ?: "", false)
|
||||
val mentions: List<Mention> = DataMessageProcessor.getMentions(message.bodyRangesList)
|
||||
val viewOnce: Boolean = message.isViewOnce
|
||||
val bodyRanges: BodyRangeList? = message.bodyRangesList.toBodyRangeList()
|
||||
val previews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(message.preview, message.body ?: "", false)
|
||||
val mentions: List<Mention> = DataMessageProcessor.getMentions(message.bodyRanges)
|
||||
val viewOnce: Boolean = message.isViewOnce == true
|
||||
val bodyRanges: BodyRangeList? = message.bodyRanges.toBodyRangeList()
|
||||
|
||||
val syncAttachments = message.attachmentsList.toPointersWithinLimit().filter {
|
||||
val syncAttachments = message.attachments.toPointersWithinLimit().filter {
|
||||
MediaUtil.SlideType.LONG_TEXT == MediaUtil.getSlideTypeFromContentType(it.contentType)
|
||||
}
|
||||
|
||||
@@ -363,7 +378,7 @@ object SyncMessageProcessor {
|
||||
recipient = toRecipient,
|
||||
body = message.body ?: "",
|
||||
attachments = syncAttachments.ifEmpty { (targetMessage as? MediaMmsMessageRecord)?.slideDeck?.asAttachments() ?: emptyList() },
|
||||
timestamp = sent.timestamp,
|
||||
timestamp = sent.timestamp!!,
|
||||
expiresIn = targetMessage.expiresIn,
|
||||
viewOnce = viewOnce,
|
||||
quote = quote,
|
||||
@@ -394,8 +409,8 @@ object SyncMessageProcessor {
|
||||
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, targetMessage.expireStarted, targetMessage.expireStarted)
|
||||
}
|
||||
if (toRecipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, toRecipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, toRecipient.id, System.currentTimeMillis())
|
||||
}
|
||||
SignalDatabase.messages.setTransactionSuccessful()
|
||||
} finally {
|
||||
@@ -412,45 +427,45 @@ object SyncMessageProcessor {
|
||||
|
||||
@Throws(MmsException::class)
|
||||
private fun handleSynchronizeSentStoryMessage(envelope: Envelope, sent: Sent) {
|
||||
log(envelope.timestamp, "Synchronize sent story message for " + sent.timestamp)
|
||||
log(envelope.timestamp!!, "Synchronize sent story message for " + sent.timestamp)
|
||||
|
||||
val manifest = SentStorySyncManifest.fromRecipientsSet(sent.storyMessageRecipientsList)
|
||||
val manifest = SentStorySyncManifest.fromRecipientsSet(sent.storyMessageRecipients)
|
||||
|
||||
if (sent.isRecipientUpdate) {
|
||||
log(envelope.timestamp, "Processing recipient update for story message and exiting...")
|
||||
SignalDatabase.storySends.applySentStoryManifest(manifest, sent.timestamp)
|
||||
if (sent.isRecipientUpdate == true) {
|
||||
log(envelope.timestamp!!, "Processing recipient update for story message and exiting...")
|
||||
SignalDatabase.storySends.applySentStoryManifest(manifest, sent.timestamp!!)
|
||||
return
|
||||
}
|
||||
|
||||
val storyMessage: StoryMessage = sent.storyMessage
|
||||
val storyMessage: StoryMessage = sent.storyMessage!!
|
||||
val distributionIds: Set<DistributionId> = manifest.getDistributionIdSet()
|
||||
val groupId: GroupId.V2? = storyMessage.group.groupId
|
||||
val groupId: GroupId.V2? = storyMessage.group?.groupId
|
||||
val textStoryBody: String? = StoryMessageProcessor.serializeTextAttachment(storyMessage)
|
||||
val bodyRanges: BodyRangeList? = storyMessage.bodyRangesList.toBodyRangeList()
|
||||
val bodyRanges: BodyRangeList? = storyMessage.bodyRanges.toBodyRangeList()
|
||||
val storyType: StoryType = storyMessage.type
|
||||
|
||||
val linkPreviews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(
|
||||
previews = if (storyMessage.textAttachment.hasPreview()) listOf(storyMessage.textAttachment.preview) else emptyList(),
|
||||
previews = listOfNotNull(storyMessage.textAttachment?.preview),
|
||||
body = "",
|
||||
isStoryEmbed = true
|
||||
)
|
||||
|
||||
val attachments: List<Attachment> = if (storyMessage.hasFileAttachment()) listOfNotNull(storyMessage.fileAttachment.toPointer()) else emptyList()
|
||||
val attachments: List<Attachment> = listOfNotNull(storyMessage.fileAttachment?.toPointer())
|
||||
|
||||
for (distributionId in distributionIds) {
|
||||
val distributionRecipientId = SignalDatabase.distributionLists.getOrCreateByDistributionId(distributionId, manifest)
|
||||
val distributionListRecipient = Recipient.resolved(distributionRecipientId)
|
||||
insertSentStoryMessage(sent, distributionListRecipient, null, textStoryBody, attachments, sent.timestamp, storyType, linkPreviews, bodyRanges)
|
||||
insertSentStoryMessage(sent, distributionListRecipient, null, textStoryBody, attachments, sent.timestamp!!, storyType, linkPreviews, bodyRanges)
|
||||
}
|
||||
|
||||
if (groupId != null) {
|
||||
val groupRecipient: Optional<RecipientId> = SignalDatabase.recipients.getByGroupId(groupId)
|
||||
if (groupRecipient.isPresent) {
|
||||
insertSentStoryMessage(sent, Recipient.resolved(groupRecipient.get()), groupId, textStoryBody, attachments, sent.timestamp, storyType, linkPreviews, bodyRanges)
|
||||
insertSentStoryMessage(sent, Recipient.resolved(groupRecipient.get()), groupId, textStoryBody, attachments, sent.timestamp!!, storyType, linkPreviews, bodyRanges)
|
||||
}
|
||||
}
|
||||
|
||||
SignalDatabase.storySends.applySentStoryManifest(manifest, sent.timestamp)
|
||||
SignalDatabase.storySends.applySentStoryManifest(manifest, sent.timestamp!!)
|
||||
}
|
||||
|
||||
@Throws(MmsException::class)
|
||||
@@ -503,8 +518,8 @@ object SyncMessageProcessor {
|
||||
attachments = allAttachments.filterNot { it.isSticker }
|
||||
|
||||
if (recipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
SignalDatabase.messages.setTransactionSuccessful()
|
||||
@@ -527,7 +542,7 @@ object SyncMessageProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val record = SignalDatabase.messages.getMessageFor(sent.timestamp, Recipient.self().id)
|
||||
val record = SignalDatabase.messages.getMessageFor(sent.timestamp!!, Recipient.self().id)
|
||||
if (record == null) {
|
||||
warn("Got recipient update for non-existing message! Skipping.")
|
||||
return
|
||||
@@ -543,9 +558,9 @@ object SyncMessageProcessor {
|
||||
|
||||
for (messageRecipientId in messageRecipientIds.keys) {
|
||||
if ((localReceipts[messageRecipientId] ?: GroupReceiptTable.STATUS_UNKNOWN) < GroupReceiptTable.STATUS_UNDELIVERED) {
|
||||
SignalDatabase.groupReceipts.update(messageRecipientId, messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp)
|
||||
SignalDatabase.groupReceipts.update(messageRecipientId, messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp!!)
|
||||
} else if (!localReceipts.containsKey(messageRecipientId)) {
|
||||
SignalDatabase.groupReceipts.insert(listOf(messageRecipientId), messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp)
|
||||
SignalDatabase.groupReceipts.insert(listOf(messageRecipientId), messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,9 +576,9 @@ object SyncMessageProcessor {
|
||||
|
||||
for (messageRecipientId in messageRecipientIds.keys) {
|
||||
if ((localReceipts[messageRecipientId] ?: GroupReceiptTable.STATUS_UNKNOWN) < GroupReceiptTable.STATUS_UNDELIVERED) {
|
||||
SignalDatabase.groupReceipts.update(messageRecipientId, messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp)
|
||||
SignalDatabase.groupReceipts.update(messageRecipientId, messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp!!)
|
||||
} else if (!localReceipts.containsKey(messageRecipientId)) {
|
||||
SignalDatabase.groupReceipts.insert(listOf(messageRecipientId), messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp)
|
||||
SignalDatabase.groupReceipts.insert(listOf(messageRecipientId), messageId, GroupReceiptTable.STATUS_UNDELIVERED, sent.timestamp!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,7 +592,7 @@ object SyncMessageProcessor {
|
||||
log(envelopeTimestamp, "Synchronize end session message.")
|
||||
|
||||
val recipient: Recipient = getSyncMessageDestination(sent)
|
||||
val outgoingEndSessionMessage: OutgoingMessage = endSessionMessage(recipient, sent.timestamp)
|
||||
val outgoingEndSessionMessage: OutgoingMessage = endSessionMessage(recipient, sent.timestamp!!)
|
||||
val threadId: Long = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
|
||||
if (!recipient.isGroup) {
|
||||
@@ -599,48 +614,56 @@ object SyncMessageProcessor {
|
||||
|
||||
@Throws(IOException::class, GroupChangeBusyException::class)
|
||||
private fun handleSynchronizeSentGv2Update(context: Context, envelope: Envelope, sent: Sent) {
|
||||
log(envelope.timestamp, "Synchronize sent GV2 update for message with timestamp " + sent.timestamp)
|
||||
log(envelope.timestamp!!, "Synchronize sent GV2 update for message with timestamp " + sent.timestamp!!)
|
||||
|
||||
val dataMessage: DataMessage = sent.message
|
||||
val groupId: GroupId.V2? = dataMessage.groupV2.groupId
|
||||
val dataMessage: DataMessage = sent.message!!
|
||||
val groupId: GroupId.V2? = dataMessage.groupV2?.groupId
|
||||
|
||||
if (MessageContentProcessor.updateGv2GroupFromServerOrP2PChange(context, envelope.timestamp, dataMessage.groupV2, SignalDatabase.groups.getGroup(GroupId.v2(dataMessage.groupV2.groupMasterKey))) == null) {
|
||||
log(envelope.timestamp, "Ignoring GV2 message for group we are not currently in $groupId")
|
||||
if (groupId == null) {
|
||||
warn(envelope.timestamp!!, "GV2 update missing group id")
|
||||
return
|
||||
}
|
||||
|
||||
if (MessageContentProcessor.updateGv2GroupFromServerOrP2PChange(context, envelope.timestamp!!, dataMessage.groupV2!!, SignalDatabase.groups.getGroup(groupId)) == null) {
|
||||
log(envelope.timestamp!!, "Ignoring GV2 message for group we are not currently in $groupId")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(MmsException::class)
|
||||
private fun handleSynchronizeSentExpirationUpdate(sent: Sent, sideEffect: Boolean = false): Long {
|
||||
log(sent.timestamp, "Synchronize sent expiration update.")
|
||||
log(sent.timestamp!!, "Synchronize sent expiration update.")
|
||||
|
||||
val groupId: GroupId? = getSyncMessageDestination(sent).groupId.orNull()
|
||||
|
||||
if (groupId != null && groupId.isV2) {
|
||||
warn(sent.timestamp, "Expiration update received for GV2. Ignoring.")
|
||||
warn(sent.timestamp!!, "Expiration update received for GV2. Ignoring.")
|
||||
return -1
|
||||
}
|
||||
|
||||
val recipient: Recipient = getSyncMessageDestination(sent)
|
||||
val expirationUpdateMessage: OutgoingMessage = expirationUpdateMessage(recipient, if (sideEffect) sent.timestamp - 1 else sent.timestamp, sent.message.expireTimer.seconds.inWholeMilliseconds)
|
||||
val expirationUpdateMessage: OutgoingMessage = expirationUpdateMessage(recipient, if (sideEffect) sent.timestamp!! - 1 else sent.timestamp!!, sent.message!!.expireTimerDuration.inWholeMilliseconds)
|
||||
val threadId: Long = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
val messageId: Long = SignalDatabase.messages.insertMessageOutbox(expirationUpdateMessage, threadId, false, null)
|
||||
|
||||
SignalDatabase.messages.markAsSent(messageId, true)
|
||||
|
||||
SignalDatabase.recipients.setExpireMessages(recipient.id, sent.message.expireTimer)
|
||||
SignalDatabase.recipients.setExpireMessages(recipient.id, sent.message!!.expireTimerDuration.inWholeSeconds.toInt())
|
||||
|
||||
return threadId
|
||||
}
|
||||
|
||||
@Throws(MmsException::class, BadGroupIdException::class)
|
||||
private fun handleSynchronizeSentStoryReply(sent: Sent, envelopeTimestamp: Long): Long {
|
||||
log(envelopeTimestamp, "Synchronize sent story reply for " + sent.timestamp)
|
||||
log(envelopeTimestamp, "Synchronize sent story reply for " + sent.timestamp!!)
|
||||
|
||||
try {
|
||||
val reaction: DataMessage.Reaction = sent.message.reaction
|
||||
val dataMessage: DataMessage = sent.message!!
|
||||
val storyContext: DataMessage.StoryContext = dataMessage.storyContext!!
|
||||
|
||||
val reaction: DataMessage.Reaction? = dataMessage.reaction
|
||||
val parentStoryId: ParentStoryId
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(sent.message.storyContext.authorAci)
|
||||
val sentTimestamp: Long = sent.message.storyContext.sentTimestamp
|
||||
val authorServiceId: ServiceId = ServiceId.parseOrThrow(storyContext.authorAci!!)
|
||||
val sentTimestamp: Long = storyContext.sentTimestamp!!
|
||||
val recipient: Recipient = getSyncMessageDestination(sent)
|
||||
var quoteModel: QuoteModel? = null
|
||||
var expiresInMillis = 0L
|
||||
@@ -651,16 +674,16 @@ object SyncMessageProcessor {
|
||||
val groupStory: Boolean = threadRecipientId != null && (SignalDatabase.groups.getGroup(threadRecipientId).orNull()?.isActive ?: false)
|
||||
var bodyRanges: BodyRangeList? = null
|
||||
|
||||
val body: String? = if (sent.message.hasReaction() && EmojiUtil.isEmoji(reaction.emoji)) {
|
||||
reaction.emoji
|
||||
} else if (sent.message.hasBody()) {
|
||||
bodyRanges = sent.message.bodyRangesList.toBodyRangeList()
|
||||
sent.message.body
|
||||
val body: String? = if (EmojiUtil.isEmoji(reaction?.emoji)) {
|
||||
reaction!!.emoji
|
||||
} else if (dataMessage.body != null) {
|
||||
bodyRanges = dataMessage.bodyRanges.toBodyRangeList()
|
||||
dataMessage.body
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (sent.message.hasGroupContext) {
|
||||
if (dataMessage.hasGroupContext) {
|
||||
parentStoryId = GroupReply(storyMessageId)
|
||||
} else if (groupStory || story.storyType.isStoryWithReplies) {
|
||||
parentStoryId = DirectReply(storyMessageId)
|
||||
@@ -672,7 +695,7 @@ object SyncMessageProcessor {
|
||||
bodyBodyRanges = story.messageRanges
|
||||
}
|
||||
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipient, quoteBody, false, story.slideDeck.asAttachments(), emptyList(), QuoteModel.Type.NORMAL, bodyBodyRanges)
|
||||
expiresInMillis = sent.message.expireTimer.seconds.inWholeMilliseconds
|
||||
expiresInMillis = dataMessage.expireTimerDuration.inWholeMilliseconds
|
||||
} else {
|
||||
warn(envelopeTimestamp, "Story has replies disabled. Dropping reply.")
|
||||
return -1L
|
||||
@@ -681,17 +704,17 @@ object SyncMessageProcessor {
|
||||
val mediaMessage = OutgoingMessage(
|
||||
recipient = recipient,
|
||||
body = body,
|
||||
timestamp = sent.timestamp,
|
||||
timestamp = sent.timestamp!!,
|
||||
expiresIn = expiresInMillis,
|
||||
parentStoryId = parentStoryId,
|
||||
isStoryReaction = sent.message.hasReaction(),
|
||||
isStoryReaction = reaction != null,
|
||||
quote = quoteModel,
|
||||
mentions = DataMessageProcessor.getMentions(sent.message.bodyRangesList),
|
||||
mentions = DataMessageProcessor.getMentions(dataMessage.bodyRanges),
|
||||
bodyRanges = bodyRanges,
|
||||
isSecure = true
|
||||
)
|
||||
|
||||
if (recipient.expiresInSeconds != sent.message.expireTimer) {
|
||||
if (recipient.expiresInSeconds != dataMessage.expireTimerDuration.inWholeSeconds.toInt()) {
|
||||
handleSynchronizeSentExpirationUpdate(sent, sideEffect = true)
|
||||
}
|
||||
|
||||
@@ -708,16 +731,16 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
SignalDatabase.messages.markAsSent(messageId, true)
|
||||
if (sent.message.expireTimer > 0) {
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp)
|
||||
if (dataMessage.expireTimerDuration > Duration.ZERO) {
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp ?: 0)
|
||||
|
||||
ApplicationDependencies
|
||||
.getExpiringMessageManager()
|
||||
.scheduleDeletion(messageId, true, sent.expirationStartTimestamp, sent.message.expireTimer.seconds.inWholeMilliseconds)
|
||||
.scheduleDeletion(messageId, true, sent.expirationStartTimestamp ?: 0, dataMessage.expireTimerDuration.inWholeMilliseconds)
|
||||
}
|
||||
if (recipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
}
|
||||
SignalDatabase.messages.setTransactionSuccessful()
|
||||
} finally {
|
||||
@@ -733,25 +756,26 @@ object SyncMessageProcessor {
|
||||
|
||||
@Throws(MmsException::class, BadGroupIdException::class)
|
||||
private fun handleSynchronizeSentMediaMessage(context: Context, sent: Sent, envelopeTimestamp: Long): Long {
|
||||
log(envelopeTimestamp, "Synchronize sent media message for " + sent.timestamp)
|
||||
log(envelopeTimestamp, "Synchronize sent media message for " + sent.timestamp!!)
|
||||
|
||||
val recipient: Recipient = getSyncMessageDestination(sent)
|
||||
val quote: QuoteModel? = DataMessageProcessor.getValidatedQuote(context, envelopeTimestamp, sent.message)
|
||||
val sticker: Attachment? = DataMessageProcessor.getStickerAttachment(envelopeTimestamp, sent.message)
|
||||
val sharedContacts: List<Contact> = DataMessageProcessor.getContacts(sent.message)
|
||||
val previews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(sent.message.previewList, sent.message.body ?: "", false)
|
||||
val mentions: List<Mention> = DataMessageProcessor.getMentions(sent.message.bodyRangesList)
|
||||
val giftBadge: GiftBadge? = if (sent.message.hasGiftBadge()) GiftBadge.newBuilder().setRedemptionToken(sent.message.giftBadge.receiptCredentialPresentation).build() else null
|
||||
val viewOnce: Boolean = sent.message.isViewOnce
|
||||
val bodyRanges: BodyRangeList? = sent.message.bodyRangesList.toBodyRangeList()
|
||||
val syncAttachments: List<Attachment> = listOfNotNull(sticker) + if (viewOnce) listOf<Attachment>(TombstoneAttachment(MediaUtil.VIEW_ONCE, false)) else sent.message.attachmentsList.toPointersWithinLimit()
|
||||
val dataMessage: DataMessage = sent.message!!
|
||||
val quote: QuoteModel? = DataMessageProcessor.getValidatedQuote(context, envelopeTimestamp, dataMessage)
|
||||
val sticker: Attachment? = DataMessageProcessor.getStickerAttachment(envelopeTimestamp, dataMessage)
|
||||
val sharedContacts: List<Contact> = DataMessageProcessor.getContacts(dataMessage)
|
||||
val previews: List<LinkPreview> = DataMessageProcessor.getLinkPreviews(dataMessage.preview, dataMessage.body ?: "", false)
|
||||
val mentions: List<Mention> = DataMessageProcessor.getMentions(dataMessage.bodyRanges)
|
||||
val giftBadge: GiftBadge? = if (dataMessage.giftBadge?.receiptCredentialPresentation != null) GiftBadge.Builder().redemptionToken(dataMessage.giftBadge!!.receiptCredentialPresentation!!).build() else null
|
||||
val viewOnce: Boolean = dataMessage.isViewOnce == true
|
||||
val bodyRanges: BodyRangeList? = dataMessage.bodyRanges.toBodyRangeList()
|
||||
val syncAttachments: List<Attachment> = listOfNotNull(sticker) + if (viewOnce) listOf<Attachment>(TombstoneAttachment(MediaUtil.VIEW_ONCE, false)) else dataMessage.attachments.toPointersWithinLimit()
|
||||
|
||||
val mediaMessage = OutgoingMessage(
|
||||
recipient = recipient,
|
||||
body = sent.message.body ?: "",
|
||||
body = dataMessage.body ?: "",
|
||||
attachments = syncAttachments,
|
||||
timestamp = sent.timestamp,
|
||||
expiresIn = sent.message.expireTimer.seconds.inWholeMilliseconds,
|
||||
timestamp = sent.timestamp!!,
|
||||
expiresIn = dataMessage.expireTimerDuration.inWholeMilliseconds,
|
||||
viewOnce = viewOnce,
|
||||
quote = quote,
|
||||
contacts = sharedContacts,
|
||||
@@ -762,7 +786,7 @@ object SyncMessageProcessor {
|
||||
isSecure = true
|
||||
)
|
||||
|
||||
if (recipient.expiresInSeconds != sent.message.expireTimer) {
|
||||
if (recipient.expiresInSeconds != dataMessage.expireTimerDuration.inWholeSeconds.toInt()) {
|
||||
handleSynchronizeSentExpirationUpdate(sent, sideEffect = true)
|
||||
}
|
||||
|
||||
@@ -784,14 +808,14 @@ object SyncMessageProcessor {
|
||||
|
||||
attachments = SignalDatabase.attachments.getAttachmentsForMessage(messageId)
|
||||
|
||||
if (sent.message.expireTimer > 0) {
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp)
|
||||
if (dataMessage.expireTimerDuration > Duration.ZERO) {
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp ?: 0)
|
||||
|
||||
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, sent.expirationStartTimestamp, sent.message.expireTimer.seconds.inWholeMilliseconds)
|
||||
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, sent.expirationStartTimestamp ?: 0, dataMessage.expireTimerDuration.inWholeMilliseconds)
|
||||
}
|
||||
if (recipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
}
|
||||
SignalDatabase.messages.setTransactionSuccessful()
|
||||
} finally {
|
||||
@@ -809,14 +833,15 @@ object SyncMessageProcessor {
|
||||
|
||||
@Throws(MmsException::class, BadGroupIdException::class)
|
||||
private fun handleSynchronizeSentTextMessage(sent: Sent, envelopeTimestamp: Long): Long {
|
||||
log(envelopeTimestamp, "Synchronize sent text message for " + sent.timestamp)
|
||||
log(envelopeTimestamp, "Synchronize sent text message for " + sent.timestamp!!)
|
||||
|
||||
val recipient = getSyncMessageDestination(sent)
|
||||
val body = sent.message.body ?: ""
|
||||
val expiresInMillis = sent.message.expireTimer.seconds.inWholeMilliseconds
|
||||
val bodyRanges = sent.message.bodyRangesList.filterNot { it.hasMentionAci() }.toBodyRangeList()
|
||||
val dataMessage: DataMessage = sent.message!!
|
||||
val body = dataMessage.body ?: ""
|
||||
val expiresInMillis = dataMessage.expireTimerDuration.inWholeMilliseconds
|
||||
val bodyRanges = dataMessage.bodyRanges.filter { it.mentionAci == null }.toBodyRangeList()
|
||||
|
||||
if (recipient.expiresInSeconds != sent.message.expireTimer) {
|
||||
if (recipient.expiresInSeconds != dataMessage.expireTimerDuration.inWholeSeconds.toInt()) {
|
||||
handleSynchronizeSentExpirationUpdate(sent, sideEffect = true)
|
||||
}
|
||||
|
||||
@@ -828,7 +853,7 @@ object SyncMessageProcessor {
|
||||
val outgoingMessage = OutgoingMessage(
|
||||
recipient = recipient,
|
||||
body = body,
|
||||
timestamp = sent.timestamp,
|
||||
timestamp = sent.timestamp!!,
|
||||
expiresIn = expiresInMillis,
|
||||
isSecure = true,
|
||||
bodyRanges = bodyRanges
|
||||
@@ -837,20 +862,20 @@ object SyncMessageProcessor {
|
||||
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null)
|
||||
updateGroupReceiptStatus(sent, messageId, recipient.requireGroupId())
|
||||
} else {
|
||||
val outgoingTextMessage = text(threadRecipient = recipient, body = body, expiresIn = expiresInMillis, sentTimeMillis = sent.timestamp, bodyRanges = bodyRanges)
|
||||
val outgoingTextMessage = text(threadRecipient = recipient, body = body, expiresIn = expiresInMillis, sentTimeMillis = sent.timestamp!!, bodyRanges = bodyRanges)
|
||||
messageId = SignalDatabase.messages.insertMessageOutbox(outgoingTextMessage, threadId, false, null)
|
||||
SignalDatabase.messages.markUnidentified(messageId, sent.isUnidentified(recipient.serviceId.orNull()))
|
||||
}
|
||||
SignalDatabase.threads.update(threadId, true)
|
||||
SignalDatabase.messages.markAsSent(messageId, true)
|
||||
if (expiresInMillis > 0) {
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp)
|
||||
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, isGroup, sent.expirationStartTimestamp, expiresInMillis)
|
||||
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp ?: 0)
|
||||
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, isGroup, sent.expirationStartTimestamp ?: 0, expiresInMillis)
|
||||
}
|
||||
|
||||
if (recipient.isSelf) {
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementDeliveryReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
SignalDatabase.messages.incrementReadReceiptCount(sent.timestamp!!, recipient.id, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
return threadId
|
||||
@@ -920,13 +945,18 @@ object SyncMessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSynchronizeViewedMessage(context: Context, viewedMessages: MutableList<SyncMessage.Viewed>, envelopeTimestamp: Long) {
|
||||
private fun handleSynchronizeViewedMessage(context: Context, viewedMessages: List<SyncMessage.Viewed>, envelopeTimestamp: Long) {
|
||||
log(envelopeTimestamp, "Synchronize view message. Count: ${viewedMessages.size}, Timestamps: ${viewedMessages.map { it.timestamp }}")
|
||||
|
||||
val records = viewedMessages
|
||||
.mapNotNull { message ->
|
||||
val author = Recipient.externalPush(ServiceId.parseOrThrow(message.senderAci)).id
|
||||
SignalDatabase.messages.getMessageFor(message.timestamp, author)
|
||||
val author = Recipient.externalPush(ServiceId.parseOrThrow(message.senderAci!!)).id
|
||||
if (message.timestamp != null) {
|
||||
SignalDatabase.messages.getMessageFor(message.timestamp!!, author)
|
||||
} else {
|
||||
warn(envelopeTimestamp, "Message timestamp null")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val toMarkViewed = records.map { it.id }
|
||||
@@ -952,8 +982,13 @@ object SyncMessageProcessor {
|
||||
private fun handleSynchronizeViewOnceOpenMessage(context: Context, openMessage: ViewOnceOpen, envelopeTimestamp: Long, earlyMessageCacheEntry: EarlyMessageCacheEntry?) {
|
||||
log(envelopeTimestamp, "Handling a view-once open for message: " + openMessage.timestamp)
|
||||
|
||||
val author: RecipientId = Recipient.externalPush(ServiceId.parseOrThrow(openMessage.senderAci)).id
|
||||
val timestamp: Long = openMessage.timestamp
|
||||
val author: RecipientId = Recipient.externalPush(ServiceId.parseOrThrow(openMessage.senderAci!!)).id
|
||||
val timestamp: Long = if (openMessage.timestamp != null) {
|
||||
openMessage.timestamp!!
|
||||
} else {
|
||||
warn(envelopeTimestamp, "Open message missing timestamp")
|
||||
return
|
||||
}
|
||||
val record: MessageRecord? = SignalDatabase.messages.getMessageFor(timestamp, author)
|
||||
|
||||
if (record != null) {
|
||||
@@ -973,7 +1008,7 @@ object SyncMessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSynchronizeVerifiedMessage(context: Context, verifiedMessage: SignalServiceProtos.Verified) {
|
||||
private fun handleSynchronizeVerifiedMessage(context: Context, verifiedMessage: Verified) {
|
||||
log("Synchronize verified message.")
|
||||
|
||||
IdentityUtil.processVerifiedMessage(context, verifiedMessage)
|
||||
@@ -985,14 +1020,13 @@ object SyncMessageProcessor {
|
||||
val jobManager = ApplicationDependencies.getJobManager()
|
||||
|
||||
for (operation in stickerPackOperations) {
|
||||
if (operation.hasPackId() && operation.hasPackKey() && operation.hasType()) {
|
||||
val packId = Hex.toStringCondensed(operation.packId.toByteArray())
|
||||
val packKey = Hex.toStringCondensed(operation.packKey.toByteArray())
|
||||
if (operation.packId != null && operation.packKey != null && operation.type != null) {
|
||||
val packId = Hex.toStringCondensed(operation.packId!!.toByteArray())
|
||||
val packKey = Hex.toStringCondensed(operation.packKey!!.toByteArray())
|
||||
|
||||
when (operation.type) {
|
||||
when (operation.type!!) {
|
||||
StickerPackOperation.Type.INSTALL -> jobManager.add(StickerPackDownloadJob.forInstall(packId, packKey, false))
|
||||
StickerPackOperation.Type.REMOVE -> SignalDatabase.stickers.uninstallPack(packId)
|
||||
else -> warn("Unknown sticker operation: ${operation.type}")
|
||||
}
|
||||
} else {
|
||||
warn("Received incomplete sticker pack operation sync.")
|
||||
@@ -1003,26 +1037,26 @@ object SyncMessageProcessor {
|
||||
private fun handleSynchronizeConfigurationMessage(context: Context, configurationMessage: Configuration, envelopeTimestamp: Long) {
|
||||
log(envelopeTimestamp, "Synchronize configuration message.")
|
||||
|
||||
if (configurationMessage.hasReadReceipts()) {
|
||||
TextSecurePreferences.setReadReceiptsEnabled(context, configurationMessage.readReceipts)
|
||||
if (configurationMessage.readReceipts != null) {
|
||||
TextSecurePreferences.setReadReceiptsEnabled(context, configurationMessage.readReceipts!!)
|
||||
}
|
||||
|
||||
if (configurationMessage.hasUnidentifiedDeliveryIndicators()) {
|
||||
TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, configurationMessage.unidentifiedDeliveryIndicators)
|
||||
if (configurationMessage.unidentifiedDeliveryIndicators != null) {
|
||||
TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, configurationMessage.unidentifiedDeliveryIndicators!!)
|
||||
}
|
||||
|
||||
if (configurationMessage.hasTypingIndicators()) {
|
||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, configurationMessage.typingIndicators)
|
||||
if (configurationMessage.typingIndicators != null) {
|
||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, configurationMessage.typingIndicators!!)
|
||||
}
|
||||
|
||||
if (configurationMessage.hasLinkPreviews()) {
|
||||
SignalStore.settings().isLinkPreviewsEnabled = configurationMessage.linkPreviews
|
||||
if (configurationMessage.linkPreviews != null) {
|
||||
SignalStore.settings().isLinkPreviewsEnabled = configurationMessage.linkPreviews!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSynchronizeBlockedListMessage(blockMessage: Blocked) {
|
||||
val addresses: List<SignalServiceAddress> = blockMessage.acisList.mapNotNull { SignalServiceAddress.fromRaw(it, null).orNull() }
|
||||
val groupIds: List<ByteArray> = blockMessage.groupIdsList.mapNotNull { it.toByteArray() }
|
||||
val addresses: List<SignalServiceAddress> = blockMessage.acis.mapNotNull { SignalServiceAddress.fromRaw(it, null).orNull() }
|
||||
val groupIds: List<ByteArray> = blockMessage.groupIds.map { it.toByteArray() }
|
||||
|
||||
SignalDatabase.recipients.applyBlockedUpdate(addresses, groupIds)
|
||||
}
|
||||
@@ -1041,10 +1075,10 @@ object SyncMessageProcessor {
|
||||
private fun handleSynchronizeMessageRequestResponse(response: MessageRequestResponse, envelopeTimestamp: Long) {
|
||||
log(envelopeTimestamp, "Synchronize message request response.")
|
||||
|
||||
val recipient: Recipient = if (response.hasThreadAci()) {
|
||||
Recipient.externalPush(ServiceId.parseOrThrow(response.threadAci))
|
||||
} else if (response.hasGroupId()) {
|
||||
val groupId: GroupId = GroupId.push(response.groupId)
|
||||
val recipient: Recipient = if (response.threadAci != null) {
|
||||
Recipient.externalPush(ServiceId.parseOrThrow(response.threadAci!!))
|
||||
} else if (response.groupId != null) {
|
||||
val groupId: GroupId = GroupId.push(response.groupId!!)
|
||||
Recipient.externalPossiblyMigratedGroup(groupId)
|
||||
} else {
|
||||
warn("Message request response was missing a thread recipient! Skipping.")
|
||||
@@ -1080,20 +1114,24 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
private fun handleSynchronizeOutgoingPayment(outgoingPayment: SyncMessage.OutgoingPayment, envelopeTimestamp: Long) {
|
||||
if (!outgoingPayment.hasMobileCoin()) {
|
||||
log("Unknown outgoing payment, ignoring.")
|
||||
log(envelopeTimestamp, "Synchronize outgoing payment.")
|
||||
|
||||
val mobileCoin = if (outgoingPayment.mobileCoin != null) {
|
||||
outgoingPayment.mobileCoin!!
|
||||
} else {
|
||||
log(envelopeTimestamp, "Unknown outgoing payment, ignoring.")
|
||||
return
|
||||
}
|
||||
|
||||
var recipientId: RecipientId? = ServiceId.parseOrNull(outgoingPayment.recipientServiceId)?.let { RecipientId.from(it) }
|
||||
|
||||
var timestamp = outgoingPayment.mobileCoin.ledgerBlockTimestamp
|
||||
var timestamp: Long = mobileCoin.ledgerBlockTimestamp ?: 0L
|
||||
if (timestamp == 0L) {
|
||||
timestamp = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
var address: MobileCoinPublicAddress? = if (outgoingPayment.mobileCoin.hasRecipientAddress()) {
|
||||
MobileCoinPublicAddress.fromBytes(outgoingPayment.mobileCoin.recipientAddress.toByteArray())
|
||||
var address: MobileCoinPublicAddress? = if (mobileCoin.recipientAddress != null) {
|
||||
MobileCoinPublicAddress.fromBytes(mobileCoin.recipientAddress!!.toByteArray())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -1112,12 +1150,12 @@ object SyncMessageProcessor {
|
||||
recipientId,
|
||||
address!!,
|
||||
timestamp,
|
||||
outgoingPayment.mobileCoin.ledgerBlockIndex,
|
||||
mobileCoin.ledgerBlockIndex!!,
|
||||
outgoingPayment.note ?: "",
|
||||
outgoingPayment.mobileCoin.amountPicoMob.toMobileCoinMoney(),
|
||||
outgoingPayment.mobileCoin.feePicoMob.toMobileCoinMoney(),
|
||||
outgoingPayment.mobileCoin.receipt.toByteArray(),
|
||||
PaymentMetaDataUtil.fromKeysAndImages(outgoingPayment.mobileCoin.outputPublicKeysList, outgoingPayment.mobileCoin.spentKeyImagesList)
|
||||
mobileCoin.amountPicoMob!!.toMobileCoinMoney(),
|
||||
mobileCoin.feePicoMob!!.toMobileCoinMoney(),
|
||||
mobileCoin.receipt!!.toByteArray(),
|
||||
PaymentMetaDataUtil.fromKeysAndImages(mobileCoin.outputPublicKeys, mobileCoin.spentKeyImages)
|
||||
)
|
||||
} catch (e: SerializationException) {
|
||||
warn(envelopeTimestamp, "Ignoring synchronized outgoing payment with bad data.", e)
|
||||
@@ -1146,13 +1184,18 @@ object SyncMessageProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val attachment: SignalServiceAttachmentPointer = contactsMessage.blob.toSignalServiceAttachmentPointer()
|
||||
if (contactsMessage.blob == null) {
|
||||
log(envelopeTimestamp, "Contact blob is null")
|
||||
return
|
||||
}
|
||||
|
||||
val attachment: SignalServiceAttachmentPointer = contactsMessage.blob!!.toSignalServiceAttachmentPointer()
|
||||
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceContactSyncJob(attachment))
|
||||
}
|
||||
|
||||
private fun handleSynchronizeCallEvent(callEvent: SyncMessage.CallEvent, envelopeTimestamp: Long) {
|
||||
if (!callEvent.hasId()) {
|
||||
if (callEvent.id == null) {
|
||||
log(envelopeTimestamp, "Synchronize call event missing call id, ignoring. type: ${callEvent.type}")
|
||||
return
|
||||
}
|
||||
@@ -1168,20 +1211,23 @@ object SyncMessageProcessor {
|
||||
if (callLogEvent.type != CallLogEvent.Type.CLEAR) {
|
||||
log(envelopeTimestamp, "Synchronize call log event has an invalid type ${callLogEvent.type}, ignoring.")
|
||||
return
|
||||
} else if (callLogEvent.timestamp == null) {
|
||||
log(envelopeTimestamp, "Synchronize call log event has null timestamp")
|
||||
return
|
||||
}
|
||||
|
||||
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(callLogEvent.timestamp)
|
||||
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(callLogEvent.timestamp)
|
||||
SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(callLogEvent.timestamp!!)
|
||||
SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(callLogEvent.timestamp!!)
|
||||
}
|
||||
|
||||
private fun handleSynchronizeCallLink(callLinkUpdate: CallLinkUpdate, envelopeTimestamp: Long) {
|
||||
if (!callLinkUpdate.hasRootKey()) {
|
||||
if (callLinkUpdate.rootKey == null) {
|
||||
log(envelopeTimestamp, "Synchronize call link missing root key, ignoring.")
|
||||
return
|
||||
}
|
||||
|
||||
val callLinkRootKey = try {
|
||||
CallLinkRootKey(callLinkUpdate.rootKey.toByteArray())
|
||||
CallLinkRootKey(callLinkUpdate.rootKey!!.toByteArray())
|
||||
} catch (e: CallException) {
|
||||
log(envelopeTimestamp, "Synchronize call link has invalid root key, ignoring.")
|
||||
return
|
||||
@@ -1193,7 +1239,7 @@ object SyncMessageProcessor {
|
||||
SignalDatabase.callLinks.updateCallLinkCredentials(
|
||||
roomId,
|
||||
CallLinkCredentials(
|
||||
callLinkUpdate.rootKey.toByteArray(),
|
||||
callLinkUpdate.rootKey!!.toByteArray(),
|
||||
callLinkUpdate.adminPassKey?.toByteArray()
|
||||
)
|
||||
)
|
||||
@@ -1216,18 +1262,18 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
private fun handleSynchronizeOneToOneCallEvent(callEvent: SyncMessage.CallEvent, envelopeTimestamp: Long) {
|
||||
val callId: Long = callEvent.id
|
||||
val timestamp: Long = callEvent.timestamp
|
||||
val callId: Long = callEvent.id!!
|
||||
val timestamp: Long = callEvent.timestamp ?: 0L
|
||||
val type: CallTable.Type? = CallTable.Type.from(callEvent.type)
|
||||
val direction: CallTable.Direction? = CallTable.Direction.from(callEvent.direction)
|
||||
val event: CallTable.Event? = CallTable.Event.from(callEvent.event)
|
||||
|
||||
if (timestamp == 0L || type == null || direction == null || event == null || !callEvent.hasConversationId()) {
|
||||
warn(envelopeTimestamp, "Call event sync message is not valid, ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
if (timestamp == 0L || type == null || direction == null || event == null || callEvent.conversationId == null) {
|
||||
warn(envelopeTimestamp, "Call event sync message is not valid, ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + (callEvent.conversationId != null))
|
||||
return
|
||||
}
|
||||
|
||||
val aci = ACI.parseOrThrow(callEvent.conversationId)
|
||||
val aci = ACI.parseOrThrow(callEvent.conversationId!!)
|
||||
val recipientId = RecipientId.from(aci)
|
||||
|
||||
log(envelopeTimestamp, "Synchronize call event call: $callId")
|
||||
@@ -1256,25 +1302,26 @@ object SyncMessageProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val callId: Long = callEvent.id
|
||||
val timestamp: Long = callEvent.timestamp
|
||||
val callId: Long = callEvent.id!!
|
||||
val timestamp: Long = callEvent.timestamp ?: 0L
|
||||
val type: CallTable.Type? = CallTable.Type.from(callEvent.type)
|
||||
val direction: CallTable.Direction? = CallTable.Direction.from(callEvent.direction)
|
||||
val event: CallTable.Event? = CallTable.Event.from(callEvent.event)
|
||||
val hasConversationId: Boolean = callEvent.conversationId != null
|
||||
|
||||
if (timestamp == 0L || type == null || direction == null || event == null || !callEvent.hasConversationId()) {
|
||||
warn(envelopeTimestamp, "Group/Ad-hoc call event sync message is not valid, ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
if (timestamp == 0L || type == null || direction == null || event == null || !hasConversationId) {
|
||||
warn(envelopeTimestamp, "Group/Ad-hoc call event sync message is not valid, ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
return
|
||||
}
|
||||
|
||||
val recipient: Recipient? = when (type) {
|
||||
CallTable.Type.AD_HOC_CALL -> {
|
||||
val callLinkRoomId = CallLinkRoomId.fromBytes(callEvent.conversationId.toByteArray())
|
||||
val callLinkRoomId = CallLinkRoomId.fromBytes(callEvent.conversationId!!.toByteArray())
|
||||
val callLink = SignalDatabase.callLinks.getOrCreateCallLinkByRoomId(callLinkRoomId)
|
||||
Recipient.resolved(callLink.recipientId)
|
||||
}
|
||||
CallTable.Type.GROUP_CALL -> {
|
||||
val groupId: GroupId = GroupId.push(callEvent.conversationId.toByteArray())
|
||||
val groupId: GroupId = GroupId.push(callEvent.conversationId!!.toByteArray())
|
||||
Recipient.externalGroupExact(groupId)
|
||||
}
|
||||
else -> {
|
||||
@@ -1292,14 +1339,14 @@ object SyncMessageProcessor {
|
||||
|
||||
if (call != null) {
|
||||
if (call.type !== type) {
|
||||
warn(envelopeTimestamp, "Group/Ad-hoc call event type mismatch, ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
warn(envelopeTimestamp, "Group/Ad-hoc call event type mismatch, ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
return
|
||||
}
|
||||
when (event) {
|
||||
CallTable.Event.DELETE -> SignalDatabase.calls.deleteGroupCall(call)
|
||||
CallTable.Event.ACCEPTED -> {
|
||||
if (call.timestamp < callEvent.timestamp) {
|
||||
SignalDatabase.calls.setTimestamp(call.callId, recipient.id, callEvent.timestamp)
|
||||
if (call.timestamp < timestamp) {
|
||||
SignalDatabase.calls.setTimestamp(call.callId, recipient.id, timestamp)
|
||||
}
|
||||
if (callEvent.direction == SyncMessage.CallEvent.Direction.INCOMING) {
|
||||
SignalDatabase.calls.acceptIncomingGroupCall(call)
|
||||
@@ -1307,15 +1354,15 @@ object SyncMessageProcessor {
|
||||
warn(envelopeTimestamp, "Invalid direction OUTGOING for event ACCEPTED")
|
||||
}
|
||||
}
|
||||
CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
else -> warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
else -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
}
|
||||
} else {
|
||||
when (event) {
|
||||
CallTable.Event.DELETE -> SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(callEvent.id, recipient.id, direction, timestamp)
|
||||
CallTable.Event.ACCEPTED -> SignalDatabase.calls.insertAcceptedGroupCall(callEvent.id, recipient.id, direction, timestamp)
|
||||
CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
else -> warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId())
|
||||
CallTable.Event.DELETE -> SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(callEvent.id!!, recipient.id, direction, timestamp)
|
||||
CallTable.Event.ACCEPTED -> SignalDatabase.calls.insertAcceptedGroupCall(callEvent.id!!, recipient.id, direction, timestamp)
|
||||
CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
else -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user