Update SignalService.proto to match shared one.

This commit is contained in:
Greyson Parrelli
2026-03-26 09:37:44 -04:00
committed by Alex Hart
parent 5ae51f844e
commit f04a0533cb
22 changed files with 413 additions and 345 deletions

View File

@@ -316,7 +316,7 @@ class DataMessageProcessorTest_polls {
private fun insertPoll(allowMultiple: Boolean = true): Long {
val envelope = MessageContentFuzzer.envelope(100)
val pollMessage = IncomingMessage(type = MessageType.NORMAL, from = alice.id, sentTimeMillis = envelope.timestamp!!, serverTimeMillis = envelope.serverTimestamp!!, receivedTimeMillis = 0, groupId = groupId)
val pollMessage = IncomingMessage(type = MessageType.NORMAL, from = alice.id, sentTimeMillis = envelope.clientTimestamp!!, serverTimeMillis = envelope.serverTimestamp!!, receivedTimeMillis = 0, groupId = groupId)
val messageId = SignalDatabase.messages.insertMessageInbox(pollMessage).get()
SignalDatabase.polls.insertPoll("question?", allowMultiple, listOf("a", "b", "c"), alice.id.toLong(), messageId.messageId)
return messageId.messageId

View File

@@ -43,7 +43,7 @@ object MessageContentFuzzer {
*/
fun envelope(timestamp: Long, serverGuid: UUID = UUID.randomUUID()): Envelope {
return Envelope.Builder()
.timestamp(timestamp)
.clientTimestamp(timestamp)
.serverTimestamp(timestamp + 5)
.serverGuidBinary(serverGuid.toByteArray().toByteString())
.build()
@@ -292,7 +292,7 @@ object MessageContentFuzzer {
body = string()
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.Builder().buildWith {
id = quoted.envelope.timestamp
id = quoted.envelope.clientTimestamp
authorAciBinary = quoted.metadata.sourceServiceId.toByteString()
text = quoted.content.dataMessage?.body
attachments(quoted.content.dataMessage?.attachments ?: emptyList())
@@ -304,7 +304,7 @@ object MessageContentFuzzer {
if (random.nextFloat() < 0.1 && quoteAble.isNotEmpty()) {
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.Builder().buildWith {
id = random.nextLong(quoted.envelope.timestamp!! - 1000000, quoted.envelope.timestamp!!)
id = random.nextLong(quoted.envelope.clientTimestamp!! - 1000000, quoted.envelope.clientTimestamp!!)
authorAciBinary = quoted.metadata.sourceServiceId.toByteString()
text = quoted.content.dataMessage?.body
}
@@ -333,7 +333,7 @@ object MessageContentFuzzer {
emoji = emojis.random(random)
remove = false
targetAuthorAciBinary = reactTo.metadata.sourceServiceId.toByteString()
targetSentTimestamp = reactTo.envelope.timestamp
targetSentTimestamp = reactTo.envelope.clientTimestamp
}
}
}

View File

@@ -70,8 +70,8 @@ object Generator {
val serverGuid = UUID.randomUUID()
return Envelope.Builder()
.type(Envelope.Type.fromValue(this.type))
.sourceDevice(1)
.timestamp(timestamp)
.sourceDeviceId(1)
.clientTimestamp(timestamp)
.serverTimestamp(timestamp + 1)
.destinationServiceId(destination.toString())
.destinationServiceIdBinary(destination.toByteString())

View File

@@ -67,9 +67,9 @@ class PushProcessMessageJobMigration : JobMigration(10) {
val envelope = Envelope.Builder()
.sourceServiceId(sourceServiceId.toString())
.sourceDevice(proto.metadata!!.senderDevice)
.sourceDeviceId(proto.metadata!!.senderDevice)
.destinationServiceId(destinationServiceId.toString())
.timestamp(proto.metadata!!.timestamp)
.clientTimestamp(proto.metadata!!.timestamp)
.serverGuid(proto.metadata!!.serverGuid)
.serverTimestamp(proto.metadata!!.serverReceivedTimestamp)

View File

@@ -155,7 +155,7 @@ class PushProcessMessageJob private constructor(
try {
messageProcessor.process(result.envelope, result.content, result.metadata, result.serverDeliveredTimestamp, localMetric = localReceiveMetric, batchCache = batchCache)
} catch (e: Exception) {
Log.e(TAG, "Failed to process message with timestamp ${result.envelope.timestamp}. Dropping.", e)
Log.e(TAG, "Failed to process message with timestamp ${result.envelope.clientTimestamp}. Dropping.", e)
}
null
}

View File

@@ -41,15 +41,15 @@ object CallMessageProcessor {
if (metadata.destinationServiceId is ServiceId.PNI) {
if (RecipientUtil.isCallRequestAccepted(senderRecipient) && callMessage.offer != null) {
log(envelope.timestamp!!, "Received call offer message at our PNI from trusted sender, responding with profile and pni signature")
log(envelope.clientTimestamp!!, "Received call offer message at our PNI from trusted sender, responding with profile and pni signature")
RecipientUtil.shareProfileIfFirstSecureMessage(senderRecipient)
ProfileKeySendJob.create(senderRecipient, false)?.let { AppDependencies.jobManager.add(it) }
}
if (callMessage.offer != null) {
log(envelope.timestamp!!, "Call message at our PNI is an offer, continuing.")
log(envelope.clientTimestamp!!, "Call message at our PNI is an offer, continuing.")
} else {
log(envelope.timestamp!!, "Call message at our PNI is not an offer, ignoring.")
log(envelope.clientTimestamp!!, "Call message at our PNI is not an offer, ignoring.")
return
}
}
@@ -65,12 +65,12 @@ object CallMessageProcessor {
}
private fun handleCallOfferMessage(envelope: Envelope, metadata: EnvelopeMetadata, offer: Offer, senderRecipientId: RecipientId, serverDeliveredTimestamp: Long) {
log(envelope.timestamp!!, "handleCallOfferMessage...")
log(envelope.clientTimestamp!!, "handleCallOfferMessage...")
val offerId = if (offer.id != null && offer.type != null && offer.opaque != null) {
offer.id!!
} else {
warn(envelope.timestamp!!, "Invalid offer, missing id, type, or opaque")
warn(envelope.clientTimestamp!!, "Invalid offer, missing id, type, or opaque")
return
}
@@ -96,12 +96,12 @@ object CallMessageProcessor {
answer: CallMessage.Answer,
senderRecipientId: RecipientId
) {
log(envelope.timestamp!!, "handleCallAnswerMessage...")
log(envelope.clientTimestamp!!, "handleCallAnswerMessage...")
val answerId = if (answer.id != null && answer.opaque != null) {
answer.id!!
} else {
warn(envelope.timestamp!!, "Invalid answer, missing id or opaque")
warn(envelope.clientTimestamp!!, "Invalid answer, missing id or opaque")
return
}
@@ -122,7 +122,7 @@ object CallMessageProcessor {
iceUpdateList: List<CallMessage.IceUpdate>,
senderRecipientId: RecipientId
) {
log(envelope.timestamp!!, "handleCallIceUpdateMessage... " + iceUpdateList.size)
log(envelope.clientTimestamp!!, "handleCallIceUpdateMessage... " + iceUpdateList.size)
val iceCandidates: MutableList<ByteArray> = ArrayList(iceUpdateList.size)
var callId: Long = -1
@@ -142,7 +142,7 @@ object CallMessageProcessor {
iceCandidates
)
} else {
warn(envelope.timestamp!!, "Invalid ice updates, all missing opaque and/or call id")
warn(envelope.clientTimestamp!!, "Invalid ice updates, all missing opaque and/or call id")
}
}
@@ -152,12 +152,12 @@ object CallMessageProcessor {
hangup: CallMessage.Hangup?,
senderRecipientId: RecipientId
) {
log(envelope.timestamp!!, "handleCallHangupMessage")
log(envelope.clientTimestamp!!, "handleCallHangupMessage")
val (hangupId: Long, hangupDeviceId: Int?) = if (hangup?.id != null) {
hangup.id!! to hangup.deviceId
} else {
warn(envelope.timestamp!!, "Invalid hangup, null message or missing id/deviceId")
warn(envelope.clientTimestamp!!, "Invalid hangup, null message or missing id/deviceId")
return
}
@@ -170,12 +170,12 @@ object CallMessageProcessor {
}
private fun handleCallBusyMessage(envelope: Envelope, metadata: EnvelopeMetadata, busy: CallMessage.Busy, senderRecipientId: RecipientId) {
log(envelope.timestamp!!, "handleCallBusyMessage")
log(envelope.clientTimestamp!!, "handleCallBusyMessage")
val busyId = if (busy.id != null) {
busy.id!!
} else {
warn(envelope.timestamp!!, "Invalid busy, missing call id")
warn(envelope.clientTimestamp!!, "Invalid busy, missing call id")
return
}
@@ -184,12 +184,12 @@ object CallMessageProcessor {
}
private fun handleCallOpaqueMessage(envelope: Envelope, metadata: EnvelopeMetadata, opaque: Opaque, senderServiceId: ServiceId, serverDeliveredTimestamp: Long) {
log(envelope.timestamp!!, "handleCallOpaqueMessage")
log(envelope.clientTimestamp!!, "handleCallOpaqueMessage")
val data = if (opaque.data_ != null) {
opaque.data_!!.toByteArray()
} else {
warn(envelope.timestamp!!, "Invalid opaque message, null data")
warn(envelope.clientTimestamp!!, "Invalid opaque message, null data")
return
}

View File

@@ -149,7 +149,7 @@ object DataMessageProcessor {
SignalTrace.beginSection("DataMessageProcessor#gv2PreProcessing")
groupProcessResult = MessageContentProcessor.handleGv2PreProcessing(
context = context,
timestamp = envelope.timestamp!!,
timestamp = envelope.clientTimestamp!!,
content = content,
metadata = metadata,
groupId = groupId,
@@ -171,7 +171,7 @@ object DataMessageProcessor {
var messageId: MessageId? = null
SignalTrace.beginSection("DataMessageProcessor#messageInsert")
when {
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, groupId, envelope.timestamp!!)
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, groupId, envelope.clientTimestamp!!)
message.isEndSession -> insertResult = handleEndSessionMessage(context, senderRecipient.id, envelope, metadata)
message.isExpirationUpdate -> insertResult = handleExpirationUpdate(envelope, metadata, senderRecipient, threadRecipient.id, groupId, message.expireTimerDuration, message.expireTimerVersion, receivedTime, false)
message.isStoryReaction -> insertResult = handleStoryReaction(context, envelope, metadata, message, senderRecipient.id, groupId)
@@ -197,7 +197,7 @@ object DataMessageProcessor {
SignalTrace.beginSection("DataMessageProcessor#postProcess")
messageId = messageId ?: insertResult?.messageId?.let { MessageId(it) }
if (messageId != null) {
log(envelope.timestamp!!, "Inserted as messageId $messageId")
log(envelope.clientTimestamp!!, "Inserted as messageId $messageId")
}
if (groupId != null) {
@@ -206,12 +206,12 @@ object DataMessageProcessor {
else -> SignalDatabase.groups.isUnknownGroup(groupId)
}
if (unknownGroup) {
handleUnknownGroupMessage(envelope.timestamp!!, message.groupV2!!)
handleUnknownGroupMessage(envelope.clientTimestamp!!, message.groupV2!!)
}
}
if (message.profileKey.isNotEmpty()) {
handleProfileKey(envelope.timestamp!!, message.profileKey!!.toByteArray(), senderRecipient)
handleProfileKey(envelope.clientTimestamp!!, message.profileKey!!.toByteArray(), senderRecipient)
}
if (groupId == null && senderRecipient.hiddenState == HiddenState.HIDDEN) {
@@ -248,11 +248,11 @@ object DataMessageProcessor {
if (insertResult != null && insertResult.threadWasNewlyCreated && !threadRecipient.isGroup && !threadRecipient.isSelf && !senderRecipient.isSystemContact) {
val timeSinceLastSync = System.currentTimeMillis() - SignalStore.misc.lastCdsForegroundSyncTime
if (timeSinceLastSync > RemoteConfig.cdsForegroundSyncInterval || timeSinceLastSync < 0) {
log(envelope.timestamp!!, "New 1:1 chat. Scheduling a CDS sync to see if they match someone in our contacts.")
log(envelope.clientTimestamp!!, "New 1:1 chat. Scheduling a CDS sync to see if they match someone in our contacts.")
AppDependencies.jobManager.add(DirectoryRefreshJob(false))
SignalStore.misc.lastCdsForegroundSyncTime = System.currentTimeMillis()
} else {
warn(envelope.timestamp!!, "New 1:1 chat, but performed a CDS sync $timeSinceLastSync ms ago, which is less than our threshold. Skipping CDS sync.")
warn(envelope.clientTimestamp!!, "New 1:1 chat, but performed a CDS sync $timeSinceLastSync ms ago, which is less than our threshold. Skipping CDS sync.")
}
}
@@ -316,11 +316,11 @@ object DataMessageProcessor {
envelope: Envelope,
metadata: EnvelopeMetadata
): InsertResult? {
log(envelope.timestamp!!, "End session message.")
log(envelope.clientTimestamp!!, "End session message.")
val incomingMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = System.currentTimeMillis(),
isUnidentified = metadata.sealedSender,
@@ -356,20 +356,20 @@ object DataMessageProcessor {
receivedTime: Long,
sideEffect: Boolean
): InsertResult? {
log(envelope.timestamp!!, "Expiration update. Side effect: $sideEffect")
log(envelope.clientTimestamp!!, "Expiration update. Side effect: $sideEffect")
if (groupId != null) {
warn(envelope.timestamp!!, "Expiration update received for GV2. Ignoring.")
warn(envelope.clientTimestamp!!, "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.clientTimestamp!!, "No change in message expiry for group. Ignoring.")
return null
}
if (expireTimerVersion != null && expireTimerVersion < senderRecipient.expireTimerVersion) {
log(envelope.timestamp!!, "Old expireTimerVersion. Received: $expireTimerVersion, Current: ${senderRecipient.expireTimerVersion}. Ignoring.")
log(envelope.clientTimestamp!!, "Old expireTimerVersion. Received: $expireTimerVersion, Current: ${senderRecipient.expireTimerVersion}. Ignoring.")
return null
}
@@ -377,7 +377,7 @@ object DataMessageProcessor {
val mediaMessage = IncomingMessage(
type = MessageType.EXPIRATION_UPDATE,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!! - if (sideEffect) 1 else 0,
sentTimeMillis = envelope.clientTimestamp!! - if (sideEffect) 1 else 0,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = expiresIn.inWholeMilliseconds,
@@ -419,7 +419,7 @@ object DataMessageProcessor {
receivedTime: Long
) {
if (threadRecipient.expiresInSeconds.toLong() != expiresIn.inWholeSeconds || ((expireTimerVersion ?: -1) > threadRecipient.expireTimerVersion)) {
warn(envelope.timestamp!!, "Message expire time didn't match thread expire time. Handling timer update.")
warn(envelope.clientTimestamp!!, "Message expire time didn't match thread expire time. Handling timer update.")
handleExpirationUpdate(envelope, metadata, senderRecipient, threadRecipient.id, groupId, expiresIn, expireTimerVersion, receivedTime, true)
}
}
@@ -433,13 +433,13 @@ object DataMessageProcessor {
senderRecipientId: RecipientId,
groupId: GroupId.V2?
): InsertResult? {
log(envelope.timestamp!!, "Story reaction.")
log(envelope.clientTimestamp!!, "Story reaction.")
val storyContext = message.storyContext!!
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.clientTimestamp!!, "Story reaction text is not a valid emoji! Ignoring the message.")
return null
}
@@ -472,18 +472,18 @@ object DataMessageProcessor {
quoteModel = QuoteModel(sentTimestamp, authorRecipientId, displayText, false, story.slideDeck.asAttachments().firstOrNull(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
expiresIn = message.expireTimerDuration
} else {
warn(envelope.timestamp!!, "Story has reactions disabled. Dropping reaction.")
warn(envelope.clientTimestamp!!, "Story has reactions disabled. Dropping reaction.")
return null
}
} catch (e: NoSuchMessageException) {
warn(envelope.timestamp!!, "Couldn't find story for reaction.", e)
warn(envelope.clientTimestamp!!, "Couldn't find story for reaction.", e)
return null
}
val mediaMessage = IncomingMessage(
type = MessageType.STORY_REACTION,
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = System.currentTimeMillis(),
parentStoryId = parentStoryId,
@@ -513,7 +513,7 @@ object DataMessageProcessor {
null
}
} else {
warn(envelope.timestamp!!, "Failed to insert story reaction")
warn(envelope.clientTimestamp!!, "Failed to insert story reaction")
null
}
} catch (e: MmsException) {
@@ -533,7 +533,7 @@ object DataMessageProcessor {
): MessageId? {
val reaction: DataMessage.Reaction = message.reaction!!
log(envelope.timestamp!!, "Handle reaction for message " + reaction.targetSentTimestamp!!)
log(envelope.clientTimestamp!!, "Handle reaction for message " + reaction.targetSentTimestamp!!)
val emoji: String? = reaction.emoji
val isRemove: Boolean = reaction.remove ?: false
@@ -541,19 +541,19 @@ object DataMessageProcessor {
val targetSentTimestamp: Long = reaction.targetSentTimestamp!!
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "Reaction was to an unknown UUID! Ignoring the message.")
warn(envelope.clientTimestamp!!, "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.clientTimestamp!!, "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.clientTimestamp!!, "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(targetAuthor.id, targetSentTimestamp, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -562,25 +562,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.clientTimestamp!!, "[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.clientTimestamp!!, "[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.clientTimestamp!!, "[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.clientTimestamp!!, "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
return null
}
@@ -601,7 +601,7 @@ object DataMessageProcessor {
fun handleRemoteDelete(context: Context, envelope: Envelope, message: DataMessage, senderRecipientId: RecipientId, earlyMessageCacheEntry: EarlyMessageCacheEntry?): MessageId? {
val delete = message.delete!!
log(envelope.timestamp!!, "Remote delete for message ${delete.targetSentTimestamp}")
log(envelope.clientTimestamp!!, "Remote delete for message ${delete.targetSentTimestamp}")
val targetSentTimestamp: Long = delete.targetSentTimestamp!!
val targetMessage: MessageRecord? = SignalDatabase.messages.getMessageFor(targetSentTimestamp, senderRecipientId)
@@ -616,7 +616,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.clientTimestamp!!, "[handleRemoteDelete] Could not find matching message! timestamp: $targetSentTimestamp author: $senderRecipientId")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipientId, targetSentTimestamp, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -624,7 +624,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.clientTimestamp!!, "[handleRemoteDelete] Invalid remote delete! deleteTime: ${envelope.serverTimestamp!!}, targetTime: ${targetMessage.serverTimestamp}, deleteAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
null
}
}
@@ -644,13 +644,13 @@ object DataMessageProcessor {
isActivatePaymentsRequest: Boolean,
isPaymentsActivated: Boolean
): InsertResult? {
log(envelope.timestamp!!, "Payment activation request: $isActivatePaymentsRequest activated: $isPaymentsActivated")
log(envelope.clientTimestamp!!, "Payment activation request: $isActivatePaymentsRequest activated: $isPaymentsActivated")
Preconditions.checkArgument(isActivatePaymentsRequest || isPaymentsActivated)
try {
val mediaMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
@@ -675,10 +675,10 @@ object DataMessageProcessor {
senderRecipientId: RecipientId,
receivedTime: Long
): InsertResult? {
log(envelope.timestamp!!, "Payment message.")
log(envelope.clientTimestamp!!, "Payment message.")
if (message.payment?.notification?.mobileCoin?.receipt == null) {
warn(envelope.timestamp!!, "Ignoring payment message without notification")
warn(envelope.clientTimestamp!!, "Ignoring payment message without notification")
return null
}
@@ -701,7 +701,7 @@ object DataMessageProcessor {
val mediaMessage = IncomingMessage(
from = senderRecipientId,
body = uuid.toString(),
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
@@ -716,9 +716,9 @@ object DataMessageProcessor {
return insertResult
}
} catch (e: PublicKeyConflictException) {
warn(envelope.timestamp!!, "Ignoring payment with public key already in database")
warn(envelope.clientTimestamp!!, "Ignoring payment with public key already in database")
} catch (e: SerializationException) {
warn(envelope.timestamp!!, "Ignoring payment with bad data.", e)
warn(envelope.clientTimestamp!!, "Ignoring payment with bad data.", e)
} catch (e: MmsException) {
throw StorageFailedException(e, metadata.sourceServiceId.toString(), metadata.sourceDeviceId)
} finally {
@@ -743,14 +743,14 @@ object DataMessageProcessor {
groupId: GroupId.V2?,
receivedTime: Long
): InsertResult? {
log(envelope.timestamp!!, "Story reply.")
log(envelope.clientTimestamp!!, "Story reply.")
val storyContext: DataMessage.StoryContext = message.storyContext!!
val authorServiceId: ServiceId = ACI.parseOrThrow(storyContext.authorAci, storyContext.authorAciBinary)
val sentTimestamp: Long = if (storyContext.sentTimestamp != null) {
storyContext.sentTimestamp!!
} else {
warn(envelope.timestamp!!, "Invalid story reply, missing sentTimestamp")
warn(envelope.clientTimestamp!!, "Invalid story reply, missing sentTimestamp")
return null
}
@@ -798,11 +798,11 @@ object DataMessageProcessor {
quoteModel = QuoteModel(sentTimestamp, storyAuthorRecipientId, displayText, false, story.slideDeck.asAttachments().firstOrNull(), emptyList(), QuoteModel.Type.NORMAL, bodyRanges)
expiresInMillis = message.expireTimerDuration
} else {
warn(envelope.timestamp!!, "Story has replies disabled. Dropping reply.")
warn(envelope.clientTimestamp!!, "Story has replies disabled. Dropping reply.")
return null
}
} catch (e: NoSuchMessageException) {
warn(envelope.timestamp!!, "Couldn't find story for reply.", e)
warn(envelope.clientTimestamp!!, "Couldn't find story for reply.", e)
return null
}
@@ -811,7 +811,7 @@ object DataMessageProcessor {
val mediaMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = System.currentTimeMillis(),
parentStoryId = parentStoryId,
@@ -843,7 +843,7 @@ object DataMessageProcessor {
null
}
} else {
warn(envelope.timestamp!!, "Failed to insert story reply.")
warn(envelope.clientTimestamp!!, "Failed to insert story reply.")
null
}
} catch (e: MmsException) {
@@ -880,7 +880,7 @@ object DataMessageProcessor {
val mediaMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
@@ -917,7 +917,7 @@ object DataMessageProcessor {
localMetrics: SignalLocalMetrics.MessageReceive?,
batchCache: BatchCache
): InsertResult? {
log(envelope.timestamp!!, "Media message.")
log(envelope.clientTimestamp!!, "Media message.")
notifyTypingStoppedFromIncomingMessage(context, senderRecipient, threadRecipient.id, metadata.sourceDeviceId)
@@ -925,11 +925,11 @@ object DataMessageProcessor {
SignalDatabase.messages.beginTransaction()
try {
val quoteModel: QuoteModel? = getValidatedQuote(context, envelope.timestamp!!, message, senderRecipient, threadRecipient)
val quoteModel: QuoteModel? = getValidatedQuote(context, envelope.clientTimestamp!!, message, senderRecipient, threadRecipient)
val contacts: List<Contact> = getContacts(message)
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 sticker: Attachment? = getStickerAttachment(envelope.clientTimestamp!!, message)
val attachments: List<Attachment> = message.attachments.toPointersWithinLimit()
val messageRanges: BodyRangeList? = if (message.bodyRanges.isNotEmpty()) message.bodyRanges.asSequence().take(BODY_RANGE_PROCESSING_LIMIT).filter { Util.allAreNull(it.mentionAci, it.mentionAciBinary) }.toList().toBodyRangeList() else null
@@ -938,7 +938,7 @@ object DataMessageProcessor {
val mediaMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
@@ -1018,7 +1018,7 @@ object DataMessageProcessor {
localMetrics: SignalLocalMetrics.MessageReceive?,
batchCache: BatchCache
): InsertResult? {
log(envelope.timestamp!!, "Text message.")
log(envelope.clientTimestamp!!, "Text message.")
val body = message.body ?: ""
@@ -1029,7 +1029,7 @@ object DataMessageProcessor {
val textMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
body = body,
@@ -1058,15 +1058,15 @@ object DataMessageProcessor {
senderRecipientId: RecipientId,
groupId: GroupId.V2?
) {
log(envelope.timestamp!!, "Group call update message.")
log(envelope.clientTimestamp!!, "Group call update message.")
if (groupId == null) {
warn(envelope.timestamp!!, "Invalid group for group call update message")
warn(envelope.clientTimestamp!!, "Invalid group for group call update message")
return
}
if (!SignalDatabase.groups.groupExists(groupId)) {
warn(envelope.timestamp!!, "Received group call update message for unknown groupId: $groupId")
warn(envelope.clientTimestamp!!, "Received group call update message for unknown groupId: $groupId")
return
}
@@ -1091,7 +1091,7 @@ object DataMessageProcessor {
groupId: GroupId.V2?,
receivedTime: Long
): InsertResult? {
log(envelope.timestamp!!, "Handle poll creation")
log(envelope.clientTimestamp!!, "Handle poll creation")
val poll: DataMessage.PollCreate = message.pollCreate!!
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient, threadRecipient, groupId, message.expireTimerDuration, message.expireTimerVersion, receivedTime)
@@ -1099,28 +1099,28 @@ object DataMessageProcessor {
if (groupId != null) {
val groupRecord = SignalDatabase.groups.getGroup(groupId).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipient.id)) {
warn(envelope.timestamp!!, "[handlePollCreate] Poll author is not in the group. author $senderRecipient")
warn(envelope.clientTimestamp!!, "[handlePollCreate] Poll author is not in the group. author $senderRecipient")
return null
}
} else if (senderRecipient.id != threadRecipient.id && senderRecipient.id != Recipient.self().id) {
warn(envelope.timestamp!!, "[handlePollCreate] Sender is not a part of the 1:1 thread!")
warn(envelope.clientTimestamp!!, "[handlePollCreate] Sender is not a part of the 1:1 thread!")
return null
}
if (poll.question == null || poll.question!!.isEmpty() || poll.question!!.length > POLL_QUESTION_CHARACTER_LIMIT) {
warn(envelope.timestamp!!, "[handlePollCreate] Poll question is invalid.")
warn(envelope.clientTimestamp!!, "[handlePollCreate] Poll question is invalid.")
return null
}
if (poll.options.isEmpty() || poll.options.size > POLL_OPTIONS_LIMIT || poll.options.any { it.isEmpty() || it.length > POLL_CHARACTER_LIMIT }) {
warn(envelope.timestamp!!, "[handlePollCreate] Poll option is invalid.")
warn(envelope.clientTimestamp!!, "[handlePollCreate] Poll option is invalid.")
return null
}
val pollMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
groupId = groupId,
@@ -1159,7 +1159,7 @@ object DataMessageProcessor {
val pollTerminate: DataMessage.PollTerminate = message.pollTerminate!!
val targetSentTimestamp = pollTerminate.targetSentTimestamp!!
log(envelope.timestamp!!, "Handle poll termination for poll $targetSentTimestamp")
log(envelope.clientTimestamp!!, "Handle poll termination for poll $targetSentTimestamp")
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient, threadRecipient, groupId, message.expireTimerDuration, message.expireTimerVersion, receivedTime)
@@ -1170,14 +1170,14 @@ object DataMessageProcessor {
val poll = SignalDatabase.polls.getPoll(messageId.id)
if (poll == null) {
warn(envelope.timestamp!!, "[handlePollTerminate] Poll was not found. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollTerminate] Poll was not found. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
val pollMessage = IncomingMessage(
type = MessageType.POLL_TERMINATE,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
groupId = groupId,
@@ -1207,11 +1207,11 @@ object DataMessageProcessor {
val pollVote: DataMessage.PollVote = message.pollVote!!
val targetSentTimestamp = pollVote.targetSentTimestamp!!
log(envelope.timestamp!!, "Handle poll vote for poll $targetSentTimestamp")
log(envelope.clientTimestamp!!, "Handle poll vote for poll $targetSentTimestamp")
val targetAuthorServiceId: ServiceId = ServiceId.parseOrThrow(pollVote.targetAuthorAciBinary!!)
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "[handlePollVote] Vote was to an unknown UUID! Ignoring the message.")
warn(envelope.clientTimestamp!!, "[handlePollVote] Vote was to an unknown UUID! Ignoring the message.")
return null
}
@@ -1223,30 +1223,30 @@ object DataMessageProcessor {
val targetMessage = SignalDatabase.messages.getMessageRecord(messageId.id)
val pollId = SignalDatabase.polls.getPollId(messageId.id)
if (pollId == null) {
warn(envelope.timestamp!!, "[handlePollVote] Poll was not found. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollVote] Poll was not found. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
val existingVoteCount = SignalDatabase.polls.getCurrentPollVoteCount(pollId, senderRecipient.id.toLong())
val currentVoteCount = pollVote.voteCount?.toLong() ?: 0
if (currentVoteCount <= existingVoteCount) {
warn(envelope.timestamp!!, "[handlePollVote] Incoming vote count was not higher. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollVote] Incoming vote count was not higher. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
val allOptionIds = SignalDatabase.polls.getPollOptionIds(pollId)
if (pollVote.optionIndexes.any { it < 0 || it >= allOptionIds.size }) {
warn(envelope.timestamp!!, "[handlePollVote] Invalid option indexes. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollVote] Invalid option indexes. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
if (!SignalDatabase.polls.canAllowMultipleVotes(pollId) && pollVote.optionIndexes.size > 1) {
warn(envelope.timestamp!!, "[handlePollVote] Can not vote multiple times. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollVote] Can not vote multiple times. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
if (SignalDatabase.polls.hasEnded(pollId)) {
warn(envelope.timestamp!!, "[handlePollVote] Poll has already ended. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
warn(envelope.clientTimestamp!!, "[handlePollVote] Poll has already ended. timestamp: $targetSentTimestamp author: ${senderRecipient.id}")
return null
}
@@ -1274,13 +1274,13 @@ object DataMessageProcessor {
earlyMessageCacheEntry: EarlyMessageCacheEntry? = null
): InsertResult? {
val pinMessage = message.pinMessage!!
log(envelope.timestamp!!, "[handlePinMessage] Pin message for " + pinMessage.targetSentTimestamp)
log(envelope.clientTimestamp!!, "[handlePinMessage] Pin message for " + pinMessage.targetSentTimestamp)
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient, threadRecipient, groupId, message.expireTimerDuration, message.expireTimerVersion, receivedTime)
val targetAuthorServiceId: ServiceId = ACI.parseOrThrow(pinMessage.targetAuthorAciBinary!!)
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "[handlePinMessage] Unknown target author! Ignoring the message.")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Unknown target author! Ignoring the message.")
return null
}
@@ -1288,7 +1288,7 @@ object DataMessageProcessor {
val targetMessage: MmsMessageRecord? = SignalDatabase.messages.getMessageFor(pinMessage.targetSentTimestamp!!, targetAuthor.id) as? MmsMessageRecord
if (targetMessage == null) {
warn(envelope.timestamp!!, "[handlePinMessage] Could not find matching message! Putting it in the early message cache. timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Could not find matching message! Putting it in the early message cache. timestamp: ${pinMessage.targetSentTimestamp}")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(targetAuthor.id, pinMessage.targetSentTimestamp!!, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1297,38 +1297,38 @@ object DataMessageProcessor {
}
if (targetMessage.isRemoteDelete) {
warn(envelope.timestamp!!, "[handlePinMessage] Found a matching message, but it's flagged as remotely deleted. timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Found a matching message, but it's flagged as remotely deleted. timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
if (targetMessage.hasGiftBadge()) {
warn(envelope.timestamp!!, "[handlePinMessage] Cannot pin a gift badge")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Cannot pin a gift badge")
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
warn(envelope.timestamp!!, "[handlePinMessage] Could not find a thread for the message! timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Could not find a thread for the message! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
if (targetThread.recipient.id != threadRecipient.id) {
warn(envelope.timestamp!!, "[handlePinMessage] Target message is in a different thread than the thread recipient! timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Target message is in a different thread than the thread recipient! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
val groupRecord = SignalDatabase.groups.getGroup(threadRecipient.id).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipient.id)) {
warn(envelope.timestamp!!, "[handlePinMessage] Sender is not in the group! timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Sender is not in the group! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
if (groupRecord != null && groupRecord.attributesAccessControl == GroupAccessControl.ONLY_ADMINS && !groupRecord.isAdmin(senderRecipient)) {
warn(envelope.timestamp!!, "[handlePinMessage] Sender needs to be an admin! timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Sender needs to be an admin! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
if (groupRecord == null && senderRecipient.id != threadRecipient.id && Recipient.self().id != senderRecipient.id) {
warn(envelope.timestamp!!, "[handlePinMessage] Sender is not a part of the 1:1 thread! timestamp: ${pinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handlePinMessage] Sender is not a part of the 1:1 thread! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
@@ -1337,7 +1337,7 @@ object DataMessageProcessor {
val pinnedMessage = IncomingMessage(
type = MessageType.PINNED_MESSAGE,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
@@ -1350,7 +1350,7 @@ object DataMessageProcessor {
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(pinnedMessage).orNull()
return if (insertResult != null) {
log(envelope.timestamp!!, "Inserted a pinned message update at ${insertResult.messageId}")
log(envelope.clientTimestamp!!, "Inserted a pinned message update at ${insertResult.messageId}")
if (duration != MessageTable.PIN_FOREVER) {
AppDependencies.pinnedMessageManager.scheduleIfNecessary()
}
@@ -1368,11 +1368,11 @@ object DataMessageProcessor {
earlyMessageCacheEntry: EarlyMessageCacheEntry? = null
): MessageId? {
val unpinMessage = message.unpinMessage!!
log(envelope.timestamp!!, "[handleUnpinMessage] Unpin message for ${unpinMessage.targetSentTimestamp}")
log(envelope.clientTimestamp!!, "[handleUnpinMessage] Unpin message for ${unpinMessage.targetSentTimestamp}")
val targetAuthorServiceId: ServiceId = ACI.parseOrThrow(unpinMessage.targetAuthorAciBinary!!)
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Unknown target author! Ignoring the message.")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Unknown target author! Ignoring the message.")
return null
}
@@ -1380,7 +1380,7 @@ object DataMessageProcessor {
val targetMessage: MmsMessageRecord? = SignalDatabase.messages.getMessageFor(unpinMessage.targetSentTimestamp!!, targetAuthor.id) as? MmsMessageRecord
if (targetMessage == null) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Could not find matching message! Putting it in the early message cache. timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Could not find matching message! Putting it in the early message cache. timestamp: ${unpinMessage.targetSentTimestamp}")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(targetAuthor.id, unpinMessage.targetSentTimestamp!!, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1389,38 +1389,38 @@ object DataMessageProcessor {
}
if (targetMessage.isRemoteDelete) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Found a matching message, but it's flagged as remotely deleted. timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Found a matching message, but it's flagged as remotely deleted. timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
if (targetMessage.hasGiftBadge()) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Cannot pin a gift badge")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Cannot pin a gift badge")
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Could not find a thread for the message! timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Could not find a thread for the message! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
if (targetThread.recipient.id != threadRecipient.id) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Target message is in a different thread than the thread recipient! timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Target message is in a different thread than the thread recipient! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
val groupRecord = SignalDatabase.groups.getGroup(threadRecipient.id).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipient.id)) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Sender is not in the group! timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Sender is not in the group! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
if (groupRecord != null && groupRecord.attributesAccessControl == GroupAccessControl.ONLY_ADMINS && !groupRecord.isAdmin(senderRecipient)) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Sender needs to be an admin! timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Sender needs to be an admin! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
if (groupRecord == null && senderRecipient.id != threadRecipient.id && Recipient.self().id != senderRecipient.id) {
warn(envelope.timestamp!!, "[handleUnpinMessage] Sender is not a part of the 1:1 thread! timestamp: ${unpinMessage.targetSentTimestamp}")
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Sender is not a part of the 1:1 thread! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
@@ -1432,25 +1432,25 @@ object DataMessageProcessor {
fun handleAdminRemoteDelete(context: Context, envelope: Envelope, message: DataMessage, senderRecipient: Recipient, threadRecipient: Recipient, earlyMessageCacheEntry: EarlyMessageCacheEntry?): MessageId? {
if (!RemoteConfig.receiveAdminDelete) {
log(envelope.timestamp!!, "Admin delete is not allowed due to remote config.")
log(envelope.clientTimestamp!!, "Admin delete is not allowed due to remote config.")
return null
}
val delete = message.adminDelete!!
log(envelope.timestamp!!, "Admin delete for message ${delete.targetSentTimestamp}")
log(envelope.clientTimestamp!!, "Admin delete for message ${delete.targetSentTimestamp}")
val targetSentTimestamp: Long = delete.targetSentTimestamp!!
val targetAuthorServiceId: ServiceId = ACI.parseOrThrow(delete.targetAuthorAciBinary!!)
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Invalid author.")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Invalid author.")
return null
}
val targetAuthor = Recipient.externalPush(targetAuthorServiceId)
val targetMessage: MessageRecord? = SignalDatabase.messages.getMessageFor(targetSentTimestamp, targetAuthor.id)
if (targetMessage == null) {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Could not find matching message! timestamp: $targetSentTimestamp")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Could not find matching message! timestamp: $targetSentTimestamp")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(targetAuthor.id, targetSentTimestamp, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1460,19 +1460,19 @@ object DataMessageProcessor {
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Could not find a thread for the message! timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Could not find a thread for the message! timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
}
val targetThreadRecipientId = targetThread.recipient.id
if (targetThreadRecipientId != threadRecipient.id) {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Target message is in a different thread than the admin delete! timestamp: $targetSentTimestamp")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Target message is in a different thread than the admin delete! timestamp: $targetSentTimestamp")
return null
}
val groupRecord = SignalDatabase.groups.getGroup(targetThreadRecipientId).orNull()
if (groupRecord == null || !groupRecord.isV2Group) {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Invalid group.")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Invalid group.")
return null
}
@@ -1481,7 +1481,7 @@ object DataMessageProcessor {
AppDependencies.messageNotifier.updateNotification(context, ConversationId.fromMessageRecord(targetMessage))
MessageId(targetMessage.id)
} else {
warn(envelope.timestamp!!, "[handleAdminRemoteDelete] Invalid admin delete! deleteTime: ${envelope.serverTimestamp!!}, targetTime: ${targetMessage.serverTimestamp}, deleteAuthor: ${senderRecipient.id}, targetAuthor: ${targetMessage.fromRecipient.id}, isAdmin: ${groupRecord.isAdmin(senderRecipient)}")
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Invalid admin delete! deleteTime: ${envelope.serverTimestamp!!}, targetTime: ${targetMessage.serverTimestamp}, deleteAuthor: ${senderRecipient.id}, targetAuthor: ${targetMessage.fromRecipient.id}, isAdmin: ${groupRecord.isAdmin(senderRecipient)}")
null
}
}
@@ -1627,7 +1627,7 @@ object DataMessageProcessor {
): MessageId? {
val targetMessage = SignalDatabase.messages.getMessageFor(targetSentTimestamp, targetAuthor.id)
if (targetMessage == null) {
warn(envelope.timestamp!!, "[handlePollValidation] Could not find matching message! Putting it in the early message cache. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
warn(envelope.clientTimestamp!!, "[handlePollValidation] Could not find matching message! Putting it in the early message cache. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipient.id, targetSentTimestamp, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1636,22 +1636,22 @@ object DataMessageProcessor {
}
if (targetMessage.isRemoteDelete) {
warn(envelope.timestamp!!, "[handlePollValidation] Found a matching message, but it's flagged as remotely deleted. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
warn(envelope.clientTimestamp!!, "[handlePollValidation] 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!!, "[handlePollValidation] Could not find a thread for the message. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
warn(envelope.clientTimestamp!!, "[handlePollValidation] Could not find a thread for the message. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
}
val groupRecord = SignalDatabase.groups.getGroup(targetThread.recipient.id).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipient.id)) {
warn(envelope.timestamp!!, "[handlePollValidation] Sender is not in the group. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
warn(envelope.clientTimestamp!!, "[handlePollValidation] Sender is not in the group. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
} else if (groupRecord == null && senderRecipient.id != targetThread.recipient.id && senderRecipient.id != Recipient.self().id) {
warn(envelope.timestamp!!, "[handlePollValidation] Sender is not a part of the 1:1 thread!")
warn(envelope.clientTimestamp!!, "[handlePollValidation] Sender is not a part of the 1:1 thread!")
return null
}

View File

@@ -50,13 +50,13 @@ object EditMessageProcessor {
) {
val editMessage = content.editMessage!!
log(envelope.timestamp!!, "[handleEditMessage] Edit message for " + editMessage.targetSentTimestamp)
log(envelope.clientTimestamp!!, "[handleEditMessage] Edit message for " + editMessage.targetSentTimestamp)
var targetMessage: MmsMessageRecord? = SignalDatabase.messages.getMessageFor(editMessage.targetSentTimestamp!!, senderRecipient.id) as? MmsMessageRecord
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.clientTimestamp!!, "[handleEditMessage] Could not find matching message! timestamp: ${editMessage.targetSentTimestamp} author: ${senderRecipient.id}")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipient.id, editMessage.targetSentTimestamp!!, earlyMessageCacheEntry)
@@ -78,12 +78,12 @@ object EditMessageProcessor {
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.clientTimestamp!!, "[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.clientTimestamp!!, content, metadata, groupId, message.groupV2!!, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
warn(envelope.clientTimestamp!!, "[handleEditMessage] Group processor indicated we should ignore this.")
return
}
@@ -187,8 +187,8 @@ object EditMessageProcessor {
val textMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.clientTimestamp!!,
receivedTimeMillis = targetMessage.dateReceived,
body = message.body,
groupId = groupId,

View File

@@ -291,8 +291,8 @@ class IncomingMessageObserver(
null
}
Envelope.Type.PREKEY_BUNDLE,
Envelope.Type.CIPHERTEXT,
Envelope.Type.PREKEY_MESSAGE,
Envelope.Type.DOUBLE_RATCHET,
Envelope.Type.UNIDENTIFIED_SENDER,
Envelope.Type.PLAINTEXT_CONTENT -> {
SignalTrace.beginSection("IncomingMessageObserver#processMessage")
@@ -343,7 +343,7 @@ class IncomingMessageObserver(
jobs += PushProcessMessageErrorJob(
result.toMessageState(),
result.errorMetadata.toExceptionMetadata(),
result.envelope.timestamp!!
result.envelope.clientTimestamp!!
)
AppDependencies.jobManager.startChain(jobs)
@@ -369,9 +369,9 @@ class IncomingMessageObserver(
val senderId = RecipientId.from(serviceId)
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!!)
Log.i(TAG, "Received server receipt. Sender: $senderId, Device: ${envelope.sourceDeviceId}, Timestamp: ${envelope.clientTimestamp}")
SignalDatabase.messages.incrementDeliveryReceiptCount(envelope.clientTimestamp!!, senderId, System.currentTimeMillis())
SignalDatabase.messageLog.deleteEntryForRecipient(envelope.clientTimestamp!!, senderId, envelope.sourceDeviceId!!)
}
private fun MessageDecryptor.Result.toMessageState(): MessageState {

View File

@@ -350,11 +350,11 @@ open class MessageContentProcessor(private val context: Context) {
val earlyCacheEntries: List<EarlyMessageCacheEntry>? = AppDependencies
.earlyMessageCache
.retrieve(senderRecipient.id, envelope.timestamp!!)
.retrieve(senderRecipient.id, envelope.clientTimestamp!!)
.orNull()
if (!processingEarlyContent && earlyCacheEntries != null) {
log(envelope.timestamp!!, "Found " + earlyCacheEntries.size + " dependent item(s) that were retrieved earlier. Processing.")
log(envelope.clientTimestamp!!, "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, batchCache)
}
@@ -442,14 +442,14 @@ open class MessageContentProcessor(private val context: Context) {
val threadRecipient = getMessageDestination(content, senderRecipient)
if (shouldIgnore(content, senderRecipient, threadRecipient)) {
log(envelope.timestamp!!, "Ignoring message.")
log(envelope.clientTimestamp!!, "Ignoring message.")
return
}
val pending: PendingRetryReceiptModel? = AppDependencies.pendingRetryReceiptCache.get(senderRecipient.id, envelope.timestamp!!)
val receivedTime: Long = handlePendingRetry(pending, envelope.timestamp!!, threadRecipient)
val pending: PendingRetryReceiptModel? = AppDependencies.pendingRetryReceiptCache.get(senderRecipient.id, envelope.clientTimestamp!!)
val receivedTime: Long = handlePendingRetry(pending, envelope.clientTimestamp!!, threadRecipient)
log(envelope.timestamp!!, "Beginning message processing. Sender: " + formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId))
log(envelope.clientTimestamp!!, "Beginning message processing. Sender: " + formatSender(senderRecipient.id, metadata.sourceServiceId, metadata.sourceDeviceId))
localMetric?.onPreProcessComplete()
when {
content.dataMessage != null -> {
@@ -482,12 +482,12 @@ open class MessageContentProcessor(private val context: Context) {
}
content.callMessage != null -> {
log(envelope.timestamp!!, "Got call message...")
log(envelope.clientTimestamp!!, "Got call message...")
val message: CallMessage = content.callMessage!!
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}")
log(envelope.clientTimestamp!!, "Ignoring call message that is not for this device! intended: ${message.destinationDeviceId}, this: ${SignalStore.account.deviceId}")
return
}
@@ -541,12 +541,12 @@ open class MessageContentProcessor(private val context: Context) {
}
else -> {
warn(envelope.timestamp!!, "Got unrecognized message!")
warn(envelope.clientTimestamp!!, "Got unrecognized message!")
}
}
if (pending != null) {
warn(envelope.timestamp!!, "Pending retry was processed. Deleting.")
warn(envelope.clientTimestamp!!, "Pending retry was processed. Deleting.")
AppDependencies.pendingRetryReceiptCache.delete(pending)
}
}
@@ -565,7 +565,7 @@ open class MessageContentProcessor(private val context: Context) {
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.clientTimestamp!!, "Seen typing indicator for non-member " + senderRecipient.id)
return
}
@@ -576,7 +576,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.clientTimestamp!!, "Couldn't find a matching thread for a typing message.")
return
}
@@ -591,19 +591,19 @@ open class MessageContentProcessor(private val context: Context) {
private fun handleRetryReceipt(envelope: Envelope, metadata: EnvelopeMetadata, decryptionErrorMessage: DecryptionErrorMessage, senderRecipient: Recipient) {
if (!RemoteConfig.retryReceipts) {
warn(envelope.timestamp!!, "[RetryReceipt] Feature flag disabled, skipping retry receipt.")
warn(envelope.clientTimestamp!!, "[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.clientTimestamp!!, "[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.clientTimestamp!!, "[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.clientTimestamp!!, "[RetryReceipt] Requester ${senderRecipient.id} somehow has no UUID! timestamp: $sentTimestamp")
return
}
@@ -626,18 +626,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.clientTimestamp!!, "[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.clientTimestamp!!, "[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.clientTimestamp!!, "[RetryReceipt-SK] Thread recipient is not a V2 group or distribution list! Skipping.")
return
}
@@ -661,7 +661,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.clientTimestamp!!, "[RetryReceipt-SK] Found MSL entry for ${requester.id} ($requesterAddress) with timestamp $sentTimestamp. Scheduling a resend.")
AppDependencies.jobManager.add(
ResendMessageJob(
messageLogEntry.recipientId,
@@ -674,7 +674,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.clientTimestamp!!, "[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.")
AppDependencies.jobManager.add(SenderKeyDistributionSendJob(requester.id, threadRecipient.id))
}
}
@@ -683,24 +683,24 @@ open class MessageContentProcessor(private val context: Context) {
var archivedSession = false
if (ServiceId.parseOrNull(envelope.destinationServiceId, envelope.destinationServiceIdBinary) is ServiceId.PNI) {
warn(envelope.timestamp!!, "[RetryReceipt-I] Destination is our PNI. Ignoring.")
warn(envelope.clientTimestamp!!, "[RetryReceipt-I] Destination is our PNI. Ignoring.")
return
}
if (decryptionErrorMessage.ratchetKey.isPresent) {
if (ratchetKeyMatches(requester, metadata.sourceDeviceId, decryptionErrorMessage.ratchetKey.get())) {
warn(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
warn(envelope.clientTimestamp!!, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
AppDependencies.protocolStore.aci().sessions().archiveSession(requester.requireServiceId(), metadata.sourceDeviceId)
archivedSession = true
} else {
log(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key does not match. Leaving the session as-is.")
log(envelope.clientTimestamp!!, "[RetryReceipt-I] Ratchet key does not match. Leaving the session as-is.")
}
} else {
warn(envelope.timestamp!!, "[RetryReceipt-I] Missing ratchet key! Can't archive session.")
warn(envelope.clientTimestamp!!, "[RetryReceipt-I] Missing ratchet key! Can't archive session.")
}
if (messageLogEntry != null) {
warn(envelope.timestamp!!, "[RetryReceipt-I] Found an entry in the MSL. Resending.")
warn(envelope.clientTimestamp!!, "[RetryReceipt-I] Found an entry in the MSL. Resending.")
AppDependencies.jobManager.add(
ResendMessageJob(
messageLogEntry.recipientId,
@@ -713,10 +713,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.clientTimestamp!!, "[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.")
AppDependencies.jobManager.add(NullMessageSendJob(requester.id))
} else {
warn(envelope.timestamp!!, "[RetryReceipt-I] Could not find an entry in the MSL. Skipping.")
warn(envelope.clientTimestamp!!, "[RetryReceipt-I] Could not find an entry in the MSL. Skipping.")
}
}

View File

@@ -142,7 +142,7 @@ object MessageDecryptor {
val followUpOperations: MutableList<FollowUpOperation> = mutableListOf()
if (envelope.type == Envelope.Type.PREKEY_BUNDLE) {
if (envelope.type == Envelope.Type.PREKEY_MESSAGE) {
Log.i(TAG, "${logPrefix(envelope)} Prekey message. Scheduling a prekey sync job.")
followUpOperations += FollowUpOperation {
PreKeysSyncJob.create().asChain()
@@ -164,7 +164,7 @@ object MessageDecryptor {
envelope.newBuilder()
.sourceServiceId(if (BuildConfig.USE_STRING_ID) cipherResult.metadata.sourceServiceId.toString() else null)
.sourceServiceIdBinary(if (RemoteConfig.useBinaryId) cipherResult.metadata.sourceServiceId.toByteString() else null)
.sourceDevice(cipherResult.metadata.sourceDeviceId)
.sourceDeviceId(cipherResult.metadata.sourceDeviceId)
.build()
} else {
envelope
@@ -255,7 +255,7 @@ object MessageDecryptor {
followUpOperations += FollowUpOperation {
Recipient.external(e.sender)?.let {
AutomaticSessionResetJob(it.id, e.senderDevice, envelope.timestamp!!).asChain()
AutomaticSessionResetJob(it.id, e.senderDevice, envelope.clientTimestamp!!).asChain()
} ?: null.logW(TAG, "${logPrefix(envelope, e)} Failed to create a recipient with the provided identifier!")
}
@@ -276,7 +276,7 @@ object MessageDecryptor {
}
is SelfSendException -> {
Log.i(TAG, "[${envelope.timestamp}] Dropping sealed sender message from self!", e)
Log.i(TAG, "[${envelope.clientTimestamp}] Dropping sealed sender message from self!", e)
Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList())
}
@@ -320,7 +320,7 @@ object MessageDecryptor {
Log.w(TAG, "${logPrefix(envelope)} Decryption error for a sync message! Enqueuing a session reset job.", true)
followUpOperations += FollowUpOperation {
AutomaticSessionResetJob(sender.id, senderDevice, envelope.timestamp!!).asChain()
AutomaticSessionResetJob(sender.id, senderDevice, envelope.clientTimestamp!!).asChain()
}
return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations)
@@ -355,7 +355,7 @@ object MessageDecryptor {
// Note: if the message is sealed sender, it's envelope type will be UNIDENTIFIED_SENDER. The only way we can currently check if the error is
// prekey-related in that situation is using a string match.
if (envelope.type == Envelope.Type.PREKEY_BUNDLE || protocolException.message?.lowercase()?.contains("prekey") == true) {
if (envelope.type == Envelope.Type.PREKEY_MESSAGE || protocolException.message?.lowercase()?.contains("prekey") == true) {
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} Got a decryption error on a prekey message. Forcing a prekey rotation before requesting the retry.", true)
PreKeysSyncJob.create(forceRotationRequested = true).asChain().then(retryJob)
} else {
@@ -392,7 +392,7 @@ object MessageDecryptor {
return@FollowUpOperation null
}
AppDependencies.pendingRetryReceiptCache.insert(sender.id, senderDevice, envelope.timestamp!!, receivedTimestamp, threadId)
AppDependencies.pendingRetryReceiptCache.insert(sender.id, senderDevice, envelope.clientTimestamp!!, receivedTimestamp, threadId)
AppDependencies.pendingRetryReceiptManager.scheduleIfNecessary()
null
}
@@ -491,26 +491,26 @@ object MessageDecryptor {
}
private fun logPrefix(envelope: Envelope): String {
return logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary)?.logString() ?: "<sealed>", envelope.sourceDevice)
return logPrefix(envelope.clientTimestamp!!, ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary)?.logString() ?: "<sealed>", envelope.sourceDeviceId)
}
private fun logPrefix(envelope: Envelope, sender: ServiceId?): String {
return logPrefix(envelope.timestamp!!, sender?.logString() ?: "?", envelope.sourceDevice)
return logPrefix(envelope.clientTimestamp!!, sender?.logString() ?: "?", envelope.sourceDeviceId)
}
private fun logPrefix(envelope: Envelope, sender: String): String {
return logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(sender)?.logString() ?: "?", envelope.sourceDevice)
return logPrefix(envelope.clientTimestamp!!, ServiceId.parseOrNull(sender)?.logString() ?: "?", envelope.sourceDeviceId)
}
private fun logPrefix(envelope: Envelope, cipherResult: SignalServiceCipherResult): String {
return logPrefix(envelope.timestamp!!, cipherResult.metadata.sourceServiceId.logString(), cipherResult.metadata.sourceDeviceId)
return logPrefix(envelope.clientTimestamp!!, cipherResult.metadata.sourceServiceId.logString(), cipherResult.metadata.sourceDeviceId)
}
private fun logPrefix(envelope: Envelope, exception: ProtocolException): String {
return if (exception.sender != null) {
logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(exception.sender)?.logString() ?: "?", exception.senderDevice)
logPrefix(envelope.clientTimestamp!!, ServiceId.parseOrNull(exception.sender)?.logString() ?: "?", exception.senderDevice)
} else {
logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary).toString(), envelope.sourceDevice)
logPrefix(envelope.clientTimestamp!!, ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary).toString(), envelope.sourceDeviceId)
}
}
@@ -531,7 +531,7 @@ object MessageDecryptor {
envelopeType = envelope.type!!.value.toCiphertextMessageType()
}
val decryptionErrorMessage: DecryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(originalContent, envelopeType, envelope.timestamp!!, protocolException.senderDevice)
val decryptionErrorMessage: DecryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(originalContent, envelopeType, envelope.clientTimestamp!!, protocolException.senderDevice)
val groupId: GroupId? = protocolException.parseGroupId(envelope)
return SendRetryReceiptJob(sender.id, Optional.ofNullable(groupId), decryptionErrorMessage)
}
@@ -541,7 +541,7 @@ object MessageDecryptor {
try {
GroupId.push(this.groupId.get())
} catch (e: BadGroupIdException) {
Log.w(TAG, "[${envelope.timestamp}] Bad groupId!", true)
Log.w(TAG, "[${envelope.clientTimestamp}] Bad groupId!", true)
null
}
} else {
@@ -551,8 +551,8 @@ 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.DOUBLE_RATCHET.value -> CiphertextMessage.WHISPER_TYPE
Envelope.Type.PREKEY_MESSAGE.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

View File

@@ -31,7 +31,7 @@ object ReceiptMessageProcessor {
ReceiptMessage.Type.DELIVERY -> handleDeliveryReceipt(envelope, metadata, receiptMessage, senderRecipient.id, batchCache)
ReceiptMessage.Type.READ -> handleReadReceipt(context, senderRecipient.id, envelope, metadata, receiptMessage, earlyMessageCacheEntry, batchCache)
ReceiptMessage.Type.VIEWED -> handleViewedReceipt(context, envelope, metadata, receiptMessage, senderRecipient.id, earlyMessageCacheEntry)
else -> warn(envelope.timestamp!!, "Unknown recipient message type ${receiptMessage.type}")
else -> warn(envelope.clientTimestamp!!, "Unknown recipient message type ${receiptMessage.type}")
}
}
@@ -43,15 +43,15 @@ object ReceiptMessageProcessor {
senderRecipientId: RecipientId,
batchCache: BatchCache
) {
log(envelope.timestamp!!, "Processing delivery receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${deliveryReceipt.timestamp.joinToString(", ")}")
log(envelope.clientTimestamp!!, "Processing delivery receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${deliveryReceipt.timestamp.joinToString(", ")}")
val stopwatch: Stopwatch? = if (VERBOSE) Stopwatch("delivery-receipt", decimalPlaces = 2) else null
SignalTrace.beginSection("ReceiptMessageProcessor#incrementDeliveryReceiptCounts")
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementDeliveryReceiptCounts(deliveryReceipt.timestamp, senderRecipientId, envelope.timestamp!!, stopwatch, batchCache.deliveryReceiptLookupCache)
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementDeliveryReceiptCounts(deliveryReceipt.timestamp, senderRecipientId, envelope.clientTimestamp!!, stopwatch, batchCache.deliveryReceiptLookupCache)
SignalTrace.endSection()
for (targetTimestamp in missingTargetTimestamps) {
warn(envelope.timestamp!!, "[handleDeliveryReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId")
warn(envelope.clientTimestamp!!, "[handleDeliveryReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId")
// Early delivery receipts are special-cased in the database methods
}
@@ -79,21 +79,21 @@ object ReceiptMessageProcessor {
batchCache: BatchCache
) {
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
log(envelope.timestamp!!, "Ignoring read receipts for IDs: " + readReceipt.timestamp.joinToString(", "))
log(envelope.clientTimestamp!!, "Ignoring read receipts for IDs: " + readReceipt.timestamp.joinToString(", "))
return
}
log(envelope.timestamp!!, "Processing read receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${readReceipt.timestamp.joinToString(", ")}")
log(envelope.clientTimestamp!!, "Processing read receipts. Sender: $senderRecipientId, Device: ${metadata.sourceDeviceId}, Timestamps: ${readReceipt.timestamp.joinToString(", ")}")
SignalTrace.beginSection("ReceiptMessageProcessor#incrementReadReceiptCounts")
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementReadReceiptCounts(readReceipt.timestamp, senderRecipientId, envelope.timestamp!!, batchCache.readReceiptLookupCache)
val missingTargetTimestamps: Set<Long> = SignalDatabase.messages.incrementReadReceiptCounts(readReceipt.timestamp, senderRecipientId, envelope.clientTimestamp!!, batchCache.readReceiptLookupCache)
SignalTrace.endSection()
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.clientTimestamp!!, "[handleReadReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt, so associating with message from self ($selfId)")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(selfId, targetTimestamp, earlyMessageCacheEntry)
}
@@ -117,18 +117,18 @@ object ReceiptMessageProcessor {
val storyViewedReceipts = SignalStore.story.viewedReceiptsEnabled
if (!readReceipts && !storyViewedReceipts) {
log(envelope.timestamp!!, "Ignoring viewed receipts for IDs: ${viewedReceipt.timestamp.joinToString(", ")}")
log(envelope.clientTimestamp!!, "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.timestamp.joinToString(", ")}")
log(envelope.clientTimestamp!!, "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.timestamp, senderRecipientId, envelope.timestamp!!)
SignalDatabase.messages.incrementViewedReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.clientTimestamp!!)
} else if (readReceipts) {
SignalDatabase.messages.incrementViewedNonStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
SignalDatabase.messages.incrementViewedNonStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.clientTimestamp!!)
} else {
SignalDatabase.messages.incrementViewedStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.timestamp!!)
SignalDatabase.messages.incrementViewedStoryReceiptCounts(viewedReceipt.timestamp, senderRecipientId, envelope.clientTimestamp!!)
}
val foundTargetTimestamps: Set<Long> = viewedReceipt.timestamp.toSet() - missingTargetTimestamps.toSet()
@@ -138,7 +138,7 @@ object ReceiptMessageProcessor {
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.clientTimestamp!!, "[handleViewedReceipt] Could not find matching message! targetTimestamp: $targetTimestamp, receiptAuthor: $senderRecipientId | Receipt so associating with message from self ($selfId)")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(selfId, targetTimestamp, earlyMessageCacheEntry)
}

View File

@@ -33,20 +33,20 @@ object StoryMessageProcessor {
fun process(envelope: Envelope, content: Content, metadata: EnvelopeMetadata, senderRecipient: Recipient, threadRecipient: Recipient) {
val storyMessage = content.storyMessage!!
log(envelope.timestamp!!, "Story message.")
log(envelope.clientTimestamp!!, "Story message.")
if (threadRecipient.isInactiveGroup) {
warn(envelope.timestamp!!, "Dropping a group story from a group we're no longer in.")
warn(envelope.clientTimestamp!!, "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.clientTimestamp!!, "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.clientTimestamp!!, "Dropping story from an untrusted source.")
return
}
@@ -64,7 +64,7 @@ object StoryMessageProcessor {
val mediaMessage = IncomingMessage(
type = MessageType.NORMAL,
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
sentTimeMillis = envelope.clientTimestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = System.currentTimeMillis(),
storyType = storyType,

View File

@@ -162,26 +162,26 @@ object SyncMessageProcessor {
when {
syncMessage.sent != null -> handleSynchronizeSentMessage(context, envelope, content, metadata, syncMessage.sent!!, senderRecipient, threadRecipient, 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.request != null -> handleSynchronizeRequestMessage(context, syncMessage.request!!, envelope.clientTimestamp!!)
syncMessage.read.isNotEmpty() -> handleSynchronizeReadMessage(context, syncMessage.read, envelope.clientTimestamp!!, earlyMessageCacheEntry)
syncMessage.viewed.isNotEmpty() -> handleSynchronizeViewedMessage(context, syncMessage.viewed, envelope.clientTimestamp!!)
syncMessage.viewOnceOpen != null -> handleSynchronizeViewOnceOpenMessage(context, syncMessage.viewOnceOpen!!, envelope.clientTimestamp!!, 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!!, envelope.timestamp!!)
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.contacts != null -> handleSynchronizeContacts(syncMessage.contacts!!, envelope.timestamp!!)
syncMessage.keys != null -> handleSynchronizeKeys(syncMessage.keys!!, 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!!)
syncMessage.deleteForMe != null -> handleSynchronizeDeleteForMe(context, syncMessage.deleteForMe!!, envelope.timestamp!!, earlyMessageCacheEntry)
syncMessage.attachmentBackfillRequest != null -> handleSynchronizeAttachmentBackfillRequest(syncMessage.attachmentBackfillRequest!!, envelope.timestamp!!)
syncMessage.attachmentBackfillResponse != null -> warn(envelope.timestamp!!, "Contains a backfill response, but we don't handle these!")
else -> warn(envelope.timestamp!!, "Contains no known sync types...")
syncMessage.stickerPackOperation.isNotEmpty() -> handleSynchronizeStickerPackOperation(syncMessage.stickerPackOperation, envelope.clientTimestamp!!)
syncMessage.configuration != null -> handleSynchronizeConfigurationMessage(context, syncMessage.configuration!!, envelope.clientTimestamp!!)
syncMessage.blocked != null -> handleSynchronizeBlockedListMessage(syncMessage.blocked!!, envelope.clientTimestamp!!)
syncMessage.fetchLatest?.type != null -> handleSynchronizeFetchMessage(syncMessage.fetchLatest!!.type!!, envelope.clientTimestamp!!)
syncMessage.messageRequestResponse != null -> handleSynchronizeMessageRequestResponse(syncMessage.messageRequestResponse!!, envelope.clientTimestamp!!)
syncMessage.outgoingPayment != null -> handleSynchronizeOutgoingPayment(syncMessage.outgoingPayment!!, envelope.clientTimestamp!!)
syncMessage.contacts != null -> handleSynchronizeContacts(syncMessage.contacts!!, envelope.clientTimestamp!!)
syncMessage.keys != null -> handleSynchronizeKeys(syncMessage.keys!!, envelope.clientTimestamp!!)
syncMessage.callEvent != null -> handleSynchronizeCallEvent(syncMessage.callEvent!!, envelope.clientTimestamp!!)
syncMessage.callLinkUpdate != null -> handleSynchronizeCallLink(syncMessage.callLinkUpdate!!, envelope.clientTimestamp!!)
syncMessage.callLogEvent != null -> handleSynchronizeCallLogEvent(syncMessage.callLogEvent!!, envelope.clientTimestamp!!)
syncMessage.deleteForMe != null -> handleSynchronizeDeleteForMe(context, syncMessage.deleteForMe!!, envelope.clientTimestamp!!, earlyMessageCacheEntry)
syncMessage.attachmentBackfillRequest != null -> handleSynchronizeAttachmentBackfillRequest(syncMessage.attachmentBackfillRequest!!, envelope.clientTimestamp!!)
syncMessage.attachmentBackfillResponse != null -> warn(envelope.clientTimestamp!!, "Contains a backfill response, but we don't handle these!")
else -> warn(envelope.clientTimestamp!!, "Contains no known sync types...")
}
}
@@ -196,7 +196,7 @@ object SyncMessageProcessor {
threadRecipient: Recipient,
earlyMessageCacheEntry: EarlyMessageCacheEntry?
) {
log(envelope.timestamp!!, "Processing sent transcript for message with ID ${sent.timestamp!!}")
log(envelope.clientTimestamp!!, "Processing sent transcript for message with ID ${sent.timestamp!!}")
try {
handlePniIdentityKeys(envelope, sent)
@@ -212,42 +212,42 @@ object SyncMessageProcessor {
}
if (sent.isRecipientUpdate == true) {
handleGroupRecipientUpdate(sent, envelope.timestamp!!)
handleGroupRecipientUpdate(sent, envelope.clientTimestamp!!)
return
}
val dataMessage = if (sent.message != null) {
sent.message!!
} else {
warn(envelope.timestamp!!, "Sync message missing nested message to sync")
warn(envelope.clientTimestamp!!, "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.clientTimestamp!!, content, metadata, groupId, dataMessage.groupV2!!, senderRecipient) == MessageContentProcessor.Gv2PreProcessResult.IGNORE) {
return
}
}
var threadId: Long = -1
when {
dataMessage.isEndSession -> threadId = handleSynchronizeSentEndSessionMessage(context, sent, envelope.timestamp!!)
dataMessage.isEndSession -> threadId = handleSynchronizeSentEndSessionMessage(context, sent, envelope.clientTimestamp!!)
dataMessage.isGroupV2Update -> {
handleSynchronizeSentGv2Update(context, envelope, sent)
threadId = SignalDatabase.threads.getOrCreateThreadIdFor(getSyncMessageDestination(sent))
}
dataMessage.groupCallUpdate != null -> DataMessageProcessor.handleGroupCallUpdateMessage(envelope, senderRecipient.id, groupId)
dataMessage.isEmptyGroupV2Message -> warn(envelope.timestamp!!, "Empty GV2 message! Doing nothing.")
dataMessage.isEmptyGroupV2Message -> warn(envelope.clientTimestamp!!, "Empty GV2 message! Doing nothing.")
dataMessage.isExpirationUpdate -> threadId = handleSynchronizeSentExpirationUpdate(sent)
dataMessage.storyContext != null -> threadId = handleSynchronizeSentStoryReply(sent, envelope.timestamp!!)
dataMessage.storyContext != null -> threadId = handleSynchronizeSentStoryReply(sent, envelope.clientTimestamp!!)
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!!, senderRecipient, threadRecipient)
dataMessage.isMediaMessage -> threadId = handleSynchronizeSentMediaMessage(context, sent, envelope.clientTimestamp!!, senderRecipient, threadRecipient)
dataMessage.pollCreate != null -> threadId = handleSynchronizedPollCreate(envelope, dataMessage, sent, senderRecipient)
dataMessage.pollVote != null -> {
DataMessageProcessor.handlePollVote(context, envelope, dataMessage, senderRecipient, earlyMessageCacheEntry)
@@ -264,11 +264,11 @@ object SyncMessageProcessor {
DataMessageProcessor.handleAdminRemoteDelete(context, envelope, dataMessage, senderRecipient, threadRecipient, earlyMessageCacheEntry)
threadId = SignalDatabase.threads.getOrCreateThreadIdFor(getSyncMessageDestination(sent))
}
else -> threadId = handleSynchronizeSentTextMessage(sent, envelope.timestamp!!)
else -> threadId = handleSynchronizeSentTextMessage(sent, envelope.clientTimestamp!!)
}
if (groupId != null && SignalDatabase.groups.isUnknownGroup(groupId)) {
DataMessageProcessor.handleUnknownGroupMessage(envelope.timestamp!!, dataMessage.groupV2!!)
DataMessageProcessor.handleUnknownGroupMessage(envelope.clientTimestamp!!, dataMessage.groupV2!!)
}
if (dataMessage.profileKey.isNotEmpty()) {
@@ -284,7 +284,7 @@ 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.clientTimestamp!!, "Got a sent transcript while in reCAPTCHA mode. Assuming we're good to message again.")
RateLimitUtil.retryAllRateLimitedMessages(context)
}
@@ -308,16 +308,16 @@ object SyncMessageProcessor {
val address = SignalProtocolAddress(pni.toString(), SignalServiceAddress.DEFAULT_DEVICE_ID)
if (AppDependencies.protocolStore.aci().identities().getIdentity(address) != null) {
log(envelope.timestamp!!, "Ignoring identity on sent transcript for $pni because we already have one.")
log(envelope.clientTimestamp!!, "Ignoring identity on sent transcript for $pni because we already have one.")
continue
}
try {
log(envelope.timestamp!!, "Saving identity from sent transcript for $pni")
log(envelope.clientTimestamp!!, "Saving identity from sent transcript for $pni")
val identityKey = IdentityKey(status.destinationPniIdentityKey!!.toByteArray())
AppDependencies.protocolStore.aci().identities().saveIdentity(address, identityKey)
} catch (e: InvalidKeyException) {
warn(envelope.timestamp!!, "Failed to deserialize identity key for $pni")
warn(envelope.clientTimestamp!!, "Failed to deserialize identity key for $pni")
}
}
}
@@ -344,7 +344,7 @@ object SyncMessageProcessor {
val senderRecipientId = senderRecipient.id
if (targetMessage == null) {
warn(envelope.timestamp!!, "[handleSynchronizeSentEditMessage] Could not find matching message! targetTimestamp: $targetSentTimestamp author: $senderRecipientId")
warn(envelope.clientTimestamp!!, "[handleSynchronizeSentEditMessage] Could not find matching message! targetTimestamp: $targetSentTimestamp author: $senderRecipientId")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipientId, targetSentTimestamp, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -358,12 +358,12 @@ object SyncMessageProcessor {
}
if (message.isMediaMessage) {
handleSynchronizeSentEditMediaMessage(targetMessage, toRecipient, sent, message, envelope.timestamp!!)
handleSynchronizeSentEditMediaMessage(targetMessage, toRecipient, sent, message, envelope.clientTimestamp!!)
} else {
handleSynchronizeSentEditTextMessage(targetMessage, toRecipient, sent, message, envelope.timestamp!!)
handleSynchronizeSentEditTextMessage(targetMessage, toRecipient, sent, message, envelope.clientTimestamp!!)
}
} else {
warn(envelope.timestamp!!, "[handleSynchronizeSentEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${targetMessage.serverTimestamp}, sendAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
warn(envelope.clientTimestamp!!, "[handleSynchronizeSentEditMessage] Invalid message edit! editTime: ${envelope.serverTimestamp}, targetTime: ${targetMessage.serverTimestamp}, sendAuthor: $senderRecipientId, targetAuthor: ${targetMessage.fromRecipient.id}")
}
}
@@ -510,12 +510,12 @@ object SyncMessageProcessor {
@Throws(MmsException::class)
private fun handleSynchronizeSentStoryMessage(envelope: Envelope, sent: Sent) {
log(envelope.timestamp!!, "Synchronize sent story message for " + sent.timestamp)
log(envelope.clientTimestamp!!, "Synchronize sent story message for " + sent.timestamp)
val manifest = SentStorySyncManifest.fromRecipientsSet(sent.storyMessageRecipients)
if (sent.isRecipientUpdate == true) {
log(envelope.timestamp!!, "Processing recipient update for story message and exiting...")
log(envelope.clientTimestamp!!, "Processing recipient update for story message and exiting...")
SignalDatabase.storySends.applySentStoryManifest(manifest, sent.timestamp!!)
return
}
@@ -686,18 +686,18 @@ 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.clientTimestamp!!, "Synchronize sent GV2 update for message with timestamp " + sent.timestamp!!)
val dataMessage: DataMessage = sent.message!!
val groupId: GroupId.V2? = dataMessage.groupV2?.groupId
if (groupId == null) {
warn(envelope.timestamp!!, "GV2 update missing group id")
warn(envelope.clientTimestamp!!, "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")
if (MessageContentProcessor.updateGv2GroupFromServerOrP2PChange(context, envelope.clientTimestamp!!, dataMessage.groupV2!!, SignalDatabase.groups.getGroup(groupId)) == null) {
log(envelope.clientTimestamp!!, "Ignoring GV2 message for group we are not currently in $groupId")
}
}
@@ -1764,7 +1764,7 @@ object SyncMessageProcessor {
sent: Sent,
senderRecipient: Recipient
): Long {
log(envelope.timestamp!!, "Synchronize sent poll creation message.")
log(envelope.clientTimestamp!!, "Synchronize sent poll creation message.")
val recipient = getSyncMessageDestination(sent)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
@@ -1795,7 +1795,7 @@ object SyncMessageProcessor {
updateGroupReceiptStatus(sent, messageId, recipient.requireGroupId())
}
log(envelope.timestamp!!, "Inserted sync poll create message as messageId $messageId")
log(envelope.clientTimestamp!!, "Inserted sync poll create message as messageId $messageId")
SignalDatabase.messages.markAsSent(messageId, true)
@@ -1814,7 +1814,7 @@ object SyncMessageProcessor {
senderRecipient: Recipient,
earlyMessageCacheEntry: EarlyMessageCacheEntry?
): Long {
log(envelope.timestamp!!, "Synchronize sent poll terminate message")
log(envelope.clientTimestamp!!, "Synchronize sent poll terminate message")
val recipient = getSyncMessageDestination(sent)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
@@ -1827,7 +1827,7 @@ object SyncMessageProcessor {
val pollTerminate = message.pollTerminate!!
val targetMessage = SignalDatabase.messages.getMessageFor(pollTerminate.targetSentTimestamp!!, Recipient.self().id)
if (targetMessage == null) {
warn(envelope.timestamp!!, "Unable to find target message for poll termination. Putting in early message cache.")
warn(envelope.clientTimestamp!!, "Unable to find target message for poll termination. Putting in early message cache.")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipient.id, pollTerminate.targetSentTimestamp!!, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1836,7 +1836,7 @@ object SyncMessageProcessor {
}
val poll = SignalDatabase.polls.getPoll(targetMessage.id)
if (poll == null) {
warn(envelope.timestamp!!, "Unable to find poll for poll termination. Dropping.")
warn(envelope.clientTimestamp!!, "Unable to find poll for poll termination. Dropping.")
return -1
}
@@ -1857,7 +1857,7 @@ object SyncMessageProcessor {
val messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, receiptStatus, null).messageId
SignalDatabase.messages.markAsSent(messageId, true)
log(envelope.timestamp!!, "Inserted sync poll end message as messageId $messageId")
log(envelope.clientTimestamp!!, "Inserted sync poll end message as messageId $messageId")
if (expiresInMillis > 0) {
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp ?: 0)
@@ -1874,7 +1874,7 @@ object SyncMessageProcessor {
senderRecipient: Recipient,
earlyMessageCacheEntry: EarlyMessageCacheEntry?
): Long {
log(envelope.timestamp!!, "Synchronize pinned message")
log(envelope.clientTimestamp!!, "Synchronize pinned message")
val recipient = getSyncMessageDestination(sent)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
@@ -1887,14 +1887,14 @@ object SyncMessageProcessor {
val pinMessage = message.pinMessage!!
val targetAuthorServiceId: ServiceId = ACI.parseOrThrow(pinMessage.targetAuthorAciBinary!!)
if (targetAuthorServiceId.isUnknown) {
warn(envelope.timestamp!!, "Unknown author")
warn(envelope.clientTimestamp!!, "Unknown author")
return -1
}
val targetAuthor = Recipient.externalPush(targetAuthorServiceId)
val targetMessage = SignalDatabase.messages.getMessageFor(pinMessage.targetSentTimestamp!!, targetAuthor.id)
if (targetMessage == null) {
warn(envelope.timestamp!!, "Unable to find target message for sync message. Putting in early message cache.")
warn(envelope.clientTimestamp!!, "Unable to find target message for sync message. Putting in early message cache.")
if (earlyMessageCacheEntry != null) {
AppDependencies.earlyMessageCache.store(senderRecipient.id, pinMessage.targetSentTimestamp!!, earlyMessageCacheEntry)
PushProcessEarlyMessagesJob.enqueue()
@@ -1903,12 +1903,12 @@ object SyncMessageProcessor {
}
if (targetMessage.hasGiftBadge()) {
warn(envelope.timestamp!!, "Cannot pin gift badge")
warn(envelope.clientTimestamp!!, "Cannot pin gift badge")
return -1
}
if (targetMessage.isRemoteDelete) {
warn(envelope.timestamp!!, "Cannot pin deleted message")
warn(envelope.clientTimestamp!!, "Cannot pin deleted message")
return -1
}
@@ -1924,7 +1924,7 @@ object SyncMessageProcessor {
val messageId = SignalDatabase.messages.insertMessageOutbox(outgoingMessage, threadId, false, GroupReceiptTable.STATUS_UNKNOWN, null).messageId
SignalDatabase.messages.markAsSent(messageId, true)
log(envelope.timestamp!!, "Inserted sync pin message as messageId $messageId")
log(envelope.clientTimestamp!!, "Inserted sync pin message as messageId $messageId")
if (expiresInMillis > 0) {
SignalDatabase.messages.markExpireStarted(messageId, sent.expirationStartTimestamp ?: 0)

View File

@@ -112,7 +112,6 @@ import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessageList;
import org.whispersystems.signalservice.internal.push.PniSignatureMessage;
import org.whispersystems.signalservice.internal.push.Preview;
import org.whispersystems.signalservice.internal.push.ProvisioningVersion;
import org.whispersystems.signalservice.internal.push.PushAttachmentData;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.ReceiptMessage;
@@ -1602,8 +1601,6 @@ public class SignalServiceMessageSender {
configurationMessage.linkPreviews(configuration.getLinkPreviews().get());
}
configurationMessage.provisioningVersion(ProvisioningVersion.CURRENT.getValue());
return container.syncMessage(syncMessage.configuration(configurationMessage.build()).build()).build();
}

View File

@@ -104,8 +104,8 @@ public interface EnvelopeContent {
int type;
switch (message.getType()) {
case CiphertextMessage.PREKEY_TYPE: type = Type.PREKEY_BUNDLE.getValue(); break;
case CiphertextMessage.WHISPER_TYPE: type = Type.CIPHERTEXT.getValue(); break;
case CiphertextMessage.PREKEY_TYPE: type = Type.PREKEY_MESSAGE.getValue(); break;
case CiphertextMessage.WHISPER_TYPE: type = Type.DOUBLE_RATCHET.getValue(); break;
default: throw new AssertionError("Bad type: " + message.getType());
}

View File

@@ -187,25 +187,25 @@ public class SignalServiceCipher {
throw new InvalidMessageStructureException("Non-UD envelope is missing a UUID!");
}
if (envelope.type == Envelope.Type.PREKEY_BUNDLE) {
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
if (envelope.type == Envelope.Type.PREKEY_MESSAGE) {
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDeviceId);
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray()));
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDeviceId, envelope.clientTimestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
ciphertextMessageType = CiphertextMessage.PREKEY_TYPE;
signalProtocolStore.clearSenderKeySharedWith(Collections.singleton(sourceAddress));
} else if (envelope.type == Envelope.Type.CIPHERTEXT) {
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
} else if (envelope.type == Envelope.Type.DOUBLE_RATCHET) {
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDeviceId);
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
paddedMessage = sessionCipher.decrypt(new SignalMessage(envelope.content.toByteArray()));
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDeviceId, envelope.clientTimestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
ciphertextMessageType = CiphertextMessage.WHISPER_TYPE;
} else if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT) {
paddedMessage = new PlaintextContent(envelope.content.toByteArray()).getBody();
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDeviceId, envelope.clientTimestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
ciphertextMessageType = CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
} else if (envelope.type == Envelope.Type.UNIDENTIFIED_SENDER) {
SignalSealedSessionCipher sealedSessionCipher = new SignalSealedSessionCipher(sessionLock, new SealedSessionCipher(signalProtocolStore, localAddress.getServiceId().getRawUuid(), localAddress.getNumber().orElse(null), localDeviceId));
@@ -215,7 +215,7 @@ public class SignalServiceCipher {
boolean needsReceipt = true;
if (sourceServiceId != null) {
Log.w(TAG, "[" + envelope.timestamp + "] Received a UD-encrypted message sent over an identified channel. Marking as needsReceipt=false");
Log.w(TAG, "[" + envelope.clientTimestamp + "] Received a UD-encrypted message sent over an identified channel. Marking as needsReceipt=false");
needsReceipt = false;
}
@@ -226,7 +226,7 @@ public class SignalServiceCipher {
}
paddedMessage = result.getPaddedMessage();
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, needsReceipt, serverGuid, groupId, destinationStr);
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.clientTimestamp, envelope.serverTimestamp, serverDeliveredTimestamp, needsReceipt, serverGuid, groupId, destinationStr);
} else {
throw new InvalidMetadataMessageException("Unknown type: " + envelope.type);
}
@@ -236,21 +236,21 @@ public class SignalServiceCipher {
return new Plaintext(metadata, data, ciphertextMessageType);
} catch (DuplicateMessageException e) {
throw new ProtocolDuplicateMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolDuplicateMessageException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (LegacyMessageException e) {
throw new ProtocolLegacyMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolLegacyMessageException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (InvalidMessageException e) {
throw new ProtocolInvalidMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolInvalidMessageException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (InvalidKeyIdException e) {
throw new ProtocolInvalidKeyIdException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolInvalidKeyIdException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (InvalidKeyException e) {
throw new ProtocolInvalidKeyException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolInvalidKeyException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (UntrustedIdentityException e) {
throw new ProtocolUntrustedIdentityException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolUntrustedIdentityException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (InvalidVersionException e) {
throw new ProtocolInvalidVersionException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolInvalidVersionException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
} catch (NoSessionException e) {
throw new ProtocolNoSessionException(e, sourceServiceId.toString(), envelope.sourceDevice);
throw new ProtocolNoSessionException(e, sourceServiceId.toString(), envelope.sourceDeviceId);
}
}

View File

@@ -83,8 +83,8 @@ object EnvelopeContentValidator {
return Result.Invalid("[DataMessage] Missing timestamp!")
}
if (dataMessage.timestamp != envelope.timestamp) {
return Result.Invalid("[DataMessage] Timestamps don't match! envelope: ${envelope.timestamp}, content: ${dataMessage.timestamp}")
if (dataMessage.timestamp != envelope.clientTimestamp) {
return Result.Invalid("[DataMessage] Timestamps don't match! envelope: ${envelope.clientTimestamp}, content: ${dataMessage.timestamp}")
}
if (dataMessage.quote != null && ACI.parseOrNull(dataMessage.quote.authorAci, dataMessage.quote.authorAciBinary).isNullOrInvalidServiceId()) {
@@ -276,8 +276,8 @@ object EnvelopeContentValidator {
private fun validateTypingMessage(envelope: Envelope, typingMessage: TypingMessage): Result {
return if (typingMessage.timestamp == null) {
return Result.Invalid("[TypingMessage] Missing timestamp!")
} else if (typingMessage.timestamp != envelope.timestamp) {
Result.Invalid("[TypingMessage] Timestamps don't match! envelope: ${envelope.timestamp}, content: ${typingMessage.timestamp}")
} else if (typingMessage.timestamp != envelope.clientTimestamp) {
Result.Invalid("[TypingMessage] Timestamps don't match! envelope: ${envelope.clientTimestamp}, content: ${typingMessage.timestamp}")
} else if (typingMessage.action == null) {
Result.Invalid("[TypingMessage] Missing action!")
} else {

View File

@@ -13,23 +13,79 @@ option java_outer_classname = "SignalServiceProtos";
message Envelope {
enum Type {
UNKNOWN = 0;
CIPHERTEXT = 1; // content => (version byte | SignalMessage{Content})
/**
* A double-ratchet message represents a "normal," "unsealed-sender" message
* encrypted using the Double Ratchet within an established Signal session.
* Double-ratchet messages include sender information in the plaintext
* portion of the `Envelope`.
*/
DOUBLE_RATCHET = 1; // content => (version byte | SignalMessage{Content})
reserved 2;
reserved "KEY_EXCHANGE";
PREKEY_BUNDLE = 3; // content => (version byte | PreKeySignalMessage{Content})
SERVER_DELIVERY_RECEIPT = 5; // legacyMessage => [] AND content => []
UNIDENTIFIED_SENDER = 6; // legacyMessage => [] AND content => ((version byte | UnidentifiedSenderMessage) OR (version byte | Multi-Recipient Sealed Sender Format))
SENDERKEY_MESSAGE = 7; // legacyMessage => [] AND content => (version byte | SenderKeyMessage)
PLAINTEXT_CONTENT = 8; // legacyMessage => [] AND content => (marker byte | Content)
/**
* A prekey message begins a new Signal session. The `content` of a prekey
* message is a superset of a double-ratchet message's `content` and
* contains the sender's identity public key and information identifying the
* pre-keys used in the message's ciphertext. Like double-ratchet messages,
* prekey messages contain sender information in the plaintext portion of
* the `Envelope`.
*/
PREKEY_MESSAGE = 3; // content => (version byte | PreKeySignalMessage{Content})
/**
* Server delivery receipts are generated by the server when
* "unsealed-sender" messages are delivered to and acknowledged by the
* destination device. Server delivery receipts identify the sender in the
* plaintext portion of the `Envelope` and have no `content`. Note that
* receipts for sealed-sender messages are generated by clients as
* `UNIDENTIFIED_SENDER` messages.
*
* Note that, with server delivery receipts, the "client timestamp" on
* the envelope refers to the timestamp of the original message (i.e. the
* message the server just delivered) and not to the time of delivery. The
* "server timestamp" refers to the time of delivery.
*/
SERVER_DELIVERY_RECEIPT = 5; // content => []
/**
* An unidentified sender message represents a message with no sender
* information in the plaintext portion of the `Envelope`. Unidentified
* sender messages always contain an additional `subtype` in their
* `content`. They may or may not be part of an existing Signal session
* (i.e. an unidentified sender message may have a "prekey message"
* subtype or may indicate an encryption error).
*/
UNIDENTIFIED_SENDER = 6; // content => ((version byte | UnidentifiedSenderMessage) OR (version byte | Multi-Recipient Sealed Sender Format))
reserved 7;
reserved "SENDERKEY_MESSAGE";
/**
* A plaintext message is used solely to convey encryption error receipts
* and never contains encrypted message content. Encryption error receipts
* must be delivered in plaintext because, encryption/decryption of a prior
* message failed and there is no reason to believe that
* encryption/decryption of subsequent messages with the same key material
* would succeed.
*
* Critically, plaintext messages never have "real" message content
* generated by users. Plaintext messages include sender information.
*/
PLAINTEXT_CONTENT = 8; // content => (marker byte | Content)
// next: 9
}
optional Type type = 1;
reserved 2; // formerly optional string sourceE164 = 2;
optional string sourceServiceId = 11;
optional uint32 sourceDevice = 7;
optional uint32 sourceDeviceId = 7;
optional string destinationServiceId = 13;
reserved 3; // formerly optional string relay = 3;
optional uint64 timestamp = 5;
optional uint64 clientTimestamp = 5;
reserved 6; // formerly optional bytes legacyMessage = 6; // Contains an encrypted DataMessage; this field could have been set historically for type 1 or 3 messages; no longer in use
optional bytes content = 8; // Contains an encrypted Content
optional string serverGuid = 9;
@@ -48,17 +104,20 @@ message Envelope {
}
message Content {
optional DataMessage dataMessage = 1;
optional SyncMessage syncMessage = 2;
optional CallMessage callMessage = 3;
optional NullMessage nullMessage = 4;
optional ReceiptMessage receiptMessage = 5;
optional TypingMessage typingMessage = 6;
oneof content {
DataMessage dataMessage = 1;
SyncMessage syncMessage = 2;
CallMessage callMessage = 3;
NullMessage nullMessage = 4;
ReceiptMessage receiptMessage = 5;
TypingMessage typingMessage = 6;
bytes /* DecryptionErrorMessage */ decryptionErrorMessage = 8;
StoryMessage storyMessage = 9;
EditMessage editMessage = 11;
}
optional bytes /* SenderKeyDistributionMessage */ senderKeyDistributionMessage = 7;
optional bytes /* DecryptionErrorMessage */ decryptionErrorMessage = 8;
optional StoryMessage storyMessage = 9;
optional PniSignatureMessage pniSignatureMessage = 10;
optional EditMessage editMessage = 11;
}
message CallMessage {
@@ -331,8 +390,8 @@ message DataMessage {
message PollVote {
optional bytes targetAuthorAciBinary = 1;
optional uint64 targetSentTimestamp = 2;
repeated uint32 optionIndexes = 3; // must be in the range [0, options.length) from the PollCreate
optional uint32 voteCount = 4; // increment this by 1 each time you vote on a given poll
repeated uint32 optionIndexes = 3;
optional uint32 voteCount = 4;
}
message PinMessage {
@@ -441,6 +500,12 @@ message TextAttachment {
}
message Gradient {
// Color ordering:
// 0 degrees: bottom-to-top
// 90 degrees: left-to-right
// 180 degrees: top-to-bottom
// 270 degrees: right-to-left
optional uint32 startColor = 1; // deprecated: this field will be removed in a future release.
optional uint32 endColor = 2; // deprecated: this field will be removed in a future release.
optional uint32 angle = 3; // degrees
@@ -553,7 +618,7 @@ message SyncMessage {
optional bool unidentifiedDeliveryIndicators = 2;
optional bool typingIndicators = 3;
reserved /* linkPreviews */ 4;
optional uint32 provisioningVersion = 5;
reserved /* provisioningVersion */ 5;
optional bool linkPreviews = 6;
}
@@ -688,7 +753,7 @@ message SyncMessage {
optional bytes rootKey = 1;
optional bytes adminPasskey = 2;
optional Type type = 3; // defaults to UPDATE
reserved 4; // was epoch field, never used
reserved /*epoch*/ 4;
}
message CallLogEvent {
@@ -785,31 +850,40 @@ message SyncMessage {
}
}
optional Sent sent = 1;
optional Contacts contacts = 2;
oneof content {
Sent sent = 1;
Contacts contacts = 2;
Request request = 4;
Blocked blocked = 6;
Verified verified = 7;
Configuration configuration = 9;
ViewOnceOpen viewOnceOpen = 11;
FetchLatest fetchLatest = 12;
Keys keys = 13;
MessageRequestResponse messageRequestResponse = 14;
OutgoingPayment outgoingPayment = 15;
PniChangeNumber pniChangeNumber = 18;
CallEvent callEvent = 19;
CallLinkUpdate callLinkUpdate = 20;
CallLogEvent callLogEvent = 21;
DeleteForMe deleteForMe = 22;
DeviceNameChange deviceNameChange = 23;
AttachmentBackfillRequest attachmentBackfillRequest = 24;
AttachmentBackfillResponse attachmentBackfillResponse = 25;
}
reserved /*groups*/ 3;
optional Request request = 4;
// Protobufs don't allow `repeated` fields to be inside of `oneof` so while
// the fields below are mutually exclusive with the rest of the values above
// we have to place them outside of `oneof`.
repeated Read read = 5;
optional Blocked blocked = 6;
optional Verified verified = 7;
optional Configuration configuration = 9;
optional bytes padding = 8;
repeated StickerPackOperation stickerPackOperation = 10;
optional ViewOnceOpen viewOnceOpen = 11;
optional FetchLatest fetchLatest = 12;
optional Keys keys = 13;
optional MessageRequestResponse messageRequestResponse = 14;
optional OutgoingPayment outgoingPayment = 15;
repeated Viewed viewed = 16;
reserved /*pniIdentity*/ 17;
optional PniChangeNumber pniChangeNumber = 18;
optional CallEvent callEvent = 19;
optional CallLinkUpdate callLinkUpdate = 20;
optional CallLogEvent callLogEvent = 21;
optional DeleteForMe deleteForMe = 22;
optional DeviceNameChange deviceNameChange = 23;
optional AttachmentBackfillRequest attachmentBackfillRequest = 24;
optional AttachmentBackfillResponse attachmentBackfillResponse = 25;
optional bytes padding = 8;
}
message AttachmentPointer {

View File

@@ -16,7 +16,7 @@ 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.GroupContextV2
import org.whispersystems.signalservice.internal.push.SyncMessage
import org.whispersystems.signalservice.internal.push.PniSignatureMessage
class BuildSizeTreeTest {
@@ -136,12 +136,12 @@ class BuildSizeTreeTest {
fun `multiple top-level fields are all included`() {
val msg = Content(
dataMessage = DataMessage(body = "hi"),
syncMessage = SyncMessage()
pniSignatureMessage = PniSignatureMessage()
)
val tree = msg.buildSizeTree("Content")
assertThat(tree).contains("dataMessage(")
assertThat(tree).contains("syncMessage(")
assertThat(tree).contains("pniSignatureMessage(")
}
@Test

View File

@@ -23,7 +23,7 @@ class EnvelopeContentValidatorTest {
@Test
fun `validate - ensure mismatched timestamps are marked invalid`() {
val envelope = Envelope(
timestamp = 1234
clientTimestamp = 1234
)
val content = Content(
@@ -172,11 +172,10 @@ class EnvelopeContentValidatorTest {
fun `validate - plaintext content via envelope type with unexpected DataMessage is invalid`() {
val envelope = Envelope(
type = Envelope.Type.PLAINTEXT_CONTENT,
timestamp = 1234
clientTimestamp = 1234
)
val content = Content(
decryptionErrorMessage = createValidDecryptionErrorMessage(),
dataMessage = DataMessage(timestamp = 1234)
)
@@ -188,11 +187,10 @@ class EnvelopeContentValidatorTest {
fun `validate - plaintext content via ciphertext message type (sealed sender) with unexpected DataMessage is invalid`() {
val envelope = Envelope(
type = Envelope.Type.UNIDENTIFIED_SENDER,
timestamp = 1234
clientTimestamp = 1234
)
val content = Content(
decryptionErrorMessage = createValidDecryptionErrorMessage(),
dataMessage = DataMessage(timestamp = 1234)
)
@@ -231,7 +229,6 @@ class EnvelopeContentValidatorTest {
)
val content = Content(
decryptionErrorMessage = createValidDecryptionErrorMessage(),
syncMessage = org.whispersystems.signalservice.internal.push.SyncMessage()
)
@@ -242,8 +239,8 @@ class EnvelopeContentValidatorTest {
@Test
fun `validate - regular encrypted message is not subject to plaintext validation`() {
val envelope = Envelope(
type = Envelope.Type.CIPHERTEXT,
timestamp = 1234
type = Envelope.Type.DOUBLE_RATCHET,
clientTimestamp = 1234
)
val content = Content(

View File

@@ -104,9 +104,9 @@ class SignalClient {
return Envelope(
sourceServiceId = aci.toString(),
sourceDevice = 1,
sourceDeviceId = 1,
destinationServiceId = to.aci.toString(),
timestamp = sentTimestamp,
clientTimestamp = sentTimestamp,
serverTimestamp = sentTimestamp,
serverGuid = serviceGuid.toString(),
type = Envelope.Type.fromValue(outgoingPushMessage.type),
@@ -139,9 +139,9 @@ class SignalClient {
return Envelope(
sourceServiceId = aci.toString(),
sourceDevice = 1,
sourceDeviceId = 1,
destinationServiceId = to.aci.toString(),
timestamp = sentTimestamp,
clientTimestamp = sentTimestamp,
serverTimestamp = sentTimestamp,
serverGuid = serverGuid.toString(),
type = Envelope.Type.fromValue(outgoingPushMessage.type),