Merge IncomingTextMessages into IncomingMessage.

This commit is contained in:
Greyson Parrelli 2023-10-25 09:52:10 -07:00 committed by Cody Henthorne
parent 23b696c9cf
commit 6b3f41d675
29 changed files with 410 additions and 1006 deletions

View file

@ -10,7 +10,7 @@ import org.signal.core.util.ThreadUtil
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.conversation.v2.ConversationActivity
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient
@ -64,7 +64,7 @@ class ConversationItemPreviewer {
attachment()
}
val message = IncomingMediaMessage(
val message = IncomingMessage(
from = other.id,
body = body,
sentTimeMillis = System.currentTimeMillis(),
@ -83,7 +83,7 @@ class ConversationItemPreviewer {
attachment()
}
val message = IncomingMediaMessage(
val message = IncomingMessage(
from = other.id,
body = body,
sentTimeMillis = System.currentTimeMillis(),

View file

@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.database
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.recipients.Recipient
import java.util.Optional
@ -55,7 +55,7 @@ object MmsHelper {
}
fun insert(
message: IncomingMediaMessage,
message: IncomingMessage,
threadId: Long
): Optional<MessageTable.InsertResult> {
return SignalDatabase.messages.insertMessageInbox(message, threadId)

View file

@ -13,7 +13,7 @@ import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
@ -73,7 +73,7 @@ class MmsTableTest_stories {
)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
@ -95,7 +95,7 @@ class MmsTableTest_stories {
// GIVEN
val sender = recipients[0]
val messageId = MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
@ -122,7 +122,7 @@ class MmsTableTest_stories {
// GIVEN
val messageIds = recipients.take(5).map {
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = it,
sentTimeMillis = 2,
serverTimeMillis = 2,
@ -154,7 +154,7 @@ class MmsTableTest_stories {
val unviewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
@ -168,7 +168,7 @@ class MmsTableTest_stories {
val viewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
@ -213,7 +213,7 @@ class MmsTableTest_stories {
fun givenNoOutgoingStories_whenICheckIsOutgoingStoryAlreadyInDatabase_thenIExpectFalse() {
// GIVEN
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = recipients[0],
sentTimeMillis = 200,
serverTimeMillis = 2,
@ -321,7 +321,7 @@ class MmsTableTest_stories {
)
MmsHelper.insert(
IncomingMediaMessage(
IncomingMessage(
from = myStory.id,
sentTimeMillis = 201,
serverTimeMillis = 201,

View file

@ -17,6 +17,7 @@ import org.junit.runner.RunWith
import org.signal.core.util.Base64
import org.signal.core.util.SqlUtil
import org.signal.core.util.exists
import org.signal.core.util.orNull
import org.signal.core.util.requireLong
import org.signal.core.util.requireNonNullString
import org.signal.core.util.select
@ -35,11 +36,10 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.FeatureFlagsAccessor
import org.thoughtcrime.securesms.util.Util
@ -947,12 +947,28 @@ class RecipientTableTest_getAndPossiblyMerge {
MatcherAssert.assertThat("Distribution list should have updated $recipientIdE164 to $recipientIdAci", updatedList.members, Matchers.containsInAnyOrder(recipientIdAci, recipientIdAciB))
}
private fun smsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingTextMessage {
return IncomingTextMessage(sender, 1, time, time, time, body, groupId, 0, true, null)
private fun smsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingMessage {
return IncomingMessage(
from = sender,
sentTimeMillis = time,
serverTimeMillis = time,
receivedTimeMillis = time,
body = body,
groupId = groupId.orNull(),
isUnidentified = true
)
}
private fun mmsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingMediaMessage {
return IncomingMediaMessage(sender, groupId, body, time, time, time, emptyList(), 0, 0, false, false, true, Optional.empty(), false, false)
private fun mmsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingMessage {
return IncomingMessage(
from = sender,
groupId = groupId.orNull(),
body = body,
sentTimeMillis = time,
receivedTimeMillis = time,
serverTimeMillis = time,
isUnidentified = true
)
}
private fun identityKey(value: Byte): IdentityKey {

View file

@ -18,12 +18,10 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.groupChange
import org.thoughtcrime.securesms.database.model.databaseprotos.groupContext
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.Optional
import java.util.UUID
@Suppress("ClassName", "TestFunctionName")
@ -272,13 +270,27 @@ class SmsDatabaseTest_collapseJoinRequestEventsIfPossible {
assertThat("latest message should be deleted", sms.getMessageRecordOrNull(latestMessage.messageId), nullValue())
}
private fun smsMessage(sender: RecipientId, body: String? = ""): IncomingTextMessage {
private fun smsMessage(sender: RecipientId, body: String? = ""): IncomingMessage {
wallClock++
return IncomingTextMessage(sender, 1, wallClock, wallClock, wallClock, body, Optional.of(groupId), 0, true, null)
return IncomingMessage(
from = sender,
sentTimeMillis = wallClock,
serverTimeMillis = wallClock,
receivedTimeMillis = wallClock,
body = body,
groupId = groupId,
isUnidentified = true
)
}
private fun groupUpdateMessage(sender: RecipientId, groupContext: DecryptedGroupV2Context): IncomingGroupUpdateMessage {
return IncomingGroupUpdateMessage(smsMessage(sender, null), groupContext)
private fun groupUpdateMessage(sender: RecipientId, groupContext: DecryptedGroupV2Context): IncomingMessage {
wallClock++
return IncomingMessage.groupUpdate(
from = sender,
timestamp = wallClock,
groupId = groupId,
groupContext = groupContext
)
}
companion object {

View file

@ -38,10 +38,8 @@ object MessageTableTestUtils {
isKeyExchangeType:${type and MessageTypes.KEY_EXCHANGE_BIT != 0L}
isIdentityVerified:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT != 0L}
isIdentityDefault:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT != 0L}
isCorruptedKeyExchange:${type and MessageTypes.KEY_EXCHANGE_CORRUPTED_BIT != 0L}
isInvalidVersionKeyExchange:${type and MessageTypes.KEY_EXCHANGE_INVALID_VERSION_BIT != 0L}
isBundleKeyExchange:${type and MessageTypes.KEY_EXCHANGE_BUNDLE_BIT != 0L}
isContentBundleKeyExchange:${type and MessageTypes.KEY_EXCHANGE_CONTENT_FORMAT != 0L}
isIdentityUpdate:${type and MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT != 0L}
isRateLimited:${type and MessageTypes.MESSAGE_RATE_LIMITED_BIT != 0L}
isExpirationTimerUpdate:${type and MessageTypes.EXPIRATION_TIMER_UPDATE_BIT != 0L}

View file

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.SyncSystemContactLinksJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.v2.ConversationId
import org.thoughtcrime.securesms.permissions.Permissions
@ -23,7 +24,6 @@ import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.TextSecurePreferences
@ -197,7 +197,7 @@ object ContactDiscovery {
Recipient.resolvedList(newUserIds)
.filter { !it.isSelf && it.hasAUserSetDisplayName(context) && !hasSession(it.id) }
.map { IncomingJoinedMessage(it.id) }
.map { IncomingMessage.contactJoined(it.id, System.currentTimeMillis()) }
.map { SignalDatabase.messages.insertMessageInbox(it) }
.filter { it.isPresent }
.map { it.get() }

View file

@ -56,6 +56,7 @@ import org.signal.core.util.requireLongOrNull
import org.signal.core.util.requireNonNullString
import org.signal.core.util.requireString
import org.signal.core.util.select
import org.signal.core.util.toInt
import org.signal.core.util.toOptional
import org.signal.core.util.toSingleLine
import org.signal.core.util.update
@ -118,7 +119,7 @@ import org.thoughtcrime.securesms.jobs.ThreadUpdateJob
import org.thoughtcrime.securesms.jobs.TrimThreadJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.MessageGroupContext
import org.thoughtcrime.securesms.mms.MmsException
import org.thoughtcrime.securesms.mms.OutgoingMessage
@ -129,8 +130,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo
import org.thoughtcrime.securesms.revealable.ViewOnceUtil
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.sms.GroupV2UpdateMessageUtil
import org.thoughtcrime.securesms.stories.Stories.isFeatureEnabled
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.JsonUtils
@ -591,22 +591,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
.run()
}
fun markAsEndSession(id: Long) {
updateTypeBitmask(id, MessageTypes.KEY_EXCHANGE_MASK, MessageTypes.END_SESSION_BIT)
}
fun markAsInvalidVersionKeyExchange(id: Long) {
updateTypeBitmask(id, 0, MessageTypes.KEY_EXCHANGE_INVALID_VERSION_BIT)
}
fun markAsDecryptFailed(id: Long) {
updateTypeBitmask(id, MessageTypes.ENCRYPTION_MASK, MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT)
}
fun markAsNoSession(id: Long) {
updateTypeBitmask(id, MessageTypes.ENCRYPTION_MASK, MessageTypes.ENCRYPTION_REMOTE_NO_SESSION_BIT)
}
fun markAsUnsupportedProtocolVersion(id: Long) {
updateTypeBitmask(id, MessageTypes.BASE_TYPE_MASK, MessageTypes.UNSUPPORTED_MESSAGE_TYPE)
}
@ -619,10 +607,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
updateTypeBitmask(id, MessageTypes.ENCRYPTION_MASK, MessageTypes.ENCRYPTION_REMOTE_LEGACY_BIT)
}
fun markAsMissedCall(id: Long, isVideoOffer: Boolean) {
updateTypeBitmask(id, MessageTypes.TOTAL_MASK, if (isVideoOffer) MessageTypes.MISSED_VIDEO_CALL_TYPE else MessageTypes.MISSED_AUDIO_CALL_TYPE)
}
fun markSmsStatus(id: Long, status: Int) {
Log.i(TAG, "Updating ID: $id to status: $status")
@ -982,132 +966,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
@JvmOverloads
fun insertMessageInbox(message: IncomingTextMessage, editedMessage: MediaMmsMessageRecord? = null, notifyObservers: Boolean = true): Optional<InsertResult> {
var type = MessageTypes.BASE_INBOX_TYPE
var tryToCollapseJoinRequestEvents = false
if (message.isJoined) {
type = type and MessageTypes.TOTAL_MASK - MessageTypes.BASE_TYPE_MASK or MessageTypes.JOINED_TYPE
} else if (message.isPreKeyBundle) {
type = type or (MessageTypes.KEY_EXCHANGE_BIT or MessageTypes.KEY_EXCHANGE_BUNDLE_BIT)
} else if (message.isSecureMessage) {
type = type or MessageTypes.SECURE_MESSAGE_BIT
} else if (message.isGroup) {
val incomingGroupUpdateMessage = message as IncomingGroupUpdateMessage
type = type or MessageTypes.SECURE_MESSAGE_BIT
if (incomingGroupUpdateMessage.isGroupV2) {
type = type or (MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
if (incomingGroupUpdateMessage.isJustAGroupLeave) {
type = type or MessageTypes.GROUP_LEAVE_BIT
} else if (incomingGroupUpdateMessage.isCancelJoinRequest) {
tryToCollapseJoinRequestEvents = true
}
} else if (incomingGroupUpdateMessage.isUpdate) {
type = type or MessageTypes.GROUP_UPDATE_BIT
} else if (incomingGroupUpdateMessage.isQuit) {
type = type or MessageTypes.GROUP_LEAVE_BIT
}
} else if (message.isEndSession) {
type = type or MessageTypes.SECURE_MESSAGE_BIT
type = type or MessageTypes.END_SESSION_BIT
}
if (message.isPush) {
type = type or MessageTypes.PUSH_MESSAGE_BIT
}
if (message.isIdentityUpdate) {
type = type or MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT
}
if (message.isContentPreKeyBundle) {
type = type or MessageTypes.KEY_EXCHANGE_CONTENT_FORMAT
}
if (message.isIdentityVerified) {
type = type or MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT
} else if (message.isIdentityDefault) {
type = type or MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT
}
val silent = message.isIdentityUpdate ||
message.isIdentityVerified ||
message.isIdentityDefault ||
message.isJustAGroupLeave || type and MessageTypes.GROUP_UPDATE_BIT > 0
val unread = !silent && (
message.isSecureMessage ||
message.isGroup ||
message.isPreKeyBundle ||
Util.isDefaultSmsProvider(context)
)
val threadId: Long = if (message.groupId == null) threads.getOrCreateThreadIdFor(message.authorId, false) else threads.getOrCreateThreadIdFor(RecipientId.from(message.groupId!!), true)
if (tryToCollapseJoinRequestEvents) {
val result = collapseJoinRequestEventsIfPossible(threadId, message as IncomingGroupUpdateMessage)
if (result.isPresent) {
return result
}
}
val values = ContentValues()
values.put(FROM_RECIPIENT_ID, message.authorId.serialize())
values.put(FROM_DEVICE_ID, message.authorDeviceId)
values.put(TO_RECIPIENT_ID, Recipient.self().id.serialize())
values.put(DATE_RECEIVED, message.receivedTimestampMillis)
values.put(DATE_SENT, message.sentTimestampMillis)
values.put(DATE_SERVER, message.serverTimestampMillis)
values.put(READ, if (unread) 0 else 1)
values.put(SMS_SUBSCRIPTION_ID, message.subscriptionId)
values.put(EXPIRES_IN, message.expiresIn)
values.put(UNIDENTIFIED, message.isUnidentified)
values.put(BODY, message.messageBody)
values.put(TYPE, type)
values.put(THREAD_ID, threadId)
values.put(SERVER_GUID, message.serverGuid)
if (editedMessage != null) {
values.put(ORIGINAL_MESSAGE_ID, editedMessage.getOriginalOrOwnMessageId().id)
} else {
values.putNull(ORIGINAL_MESSAGE_ID)
}
val messageId: Long = writableDatabase.withinTransaction {
val id = writableDatabase.insert(TABLE_NAME, null, values)
if (id < 0) {
Log.w(TAG, "Failed to insert text message (${message.sentTimestampMillis}, ${message.authorId}, ThreadId::$threadId)! Likely a duplicate.")
} else {
if (unread && editedMessage == null) {
threads.incrementUnread(threadId, 1, 0)
}
}
id
}
if (messageId < 0) {
return Optional.empty()
}
threads.markAsActiveEarly(threadId)
if (!silent) {
ThreadUpdateJob.enqueue(threadId)
TrimThreadJob.enqueueAsync(threadId)
}
if (notifyObservers) {
notifyConversationListeners(threadId)
}
return Optional.of(InsertResult(messageId, threadId))
}
fun insertEditMessageInbox(threadId: Long, mediaMessage: IncomingMediaMessage, targetMessage: MediaMmsMessageRecord): Optional<InsertResult> {
val insertResult = insertMessageInbox(retrieved = mediaMessage, candidateThreadId = threadId, editedMessage = targetMessage, notifyObservers = false)
fun insertEditMessageInbox(mediaMessage: IncomingMessage, targetMessage: MediaMmsMessageRecord): Optional<InsertResult> {
val insertResult = insertMessageInbox(retrieved = mediaMessage, editedMessage = targetMessage, notifyObservers = false)
if (insertResult.isPresent) {
val (messageId) = insertResult.get()
@ -1129,29 +989,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
return insertResult
}
fun insertEditMessageInbox(textMessage: IncomingTextMessage, targetMessage: MediaMmsMessageRecord): Optional<InsertResult> {
val insertResult = insertMessageInbox(message = textMessage, editedMessage = targetMessage, notifyObservers = false)
if (insertResult.isPresent) {
val (messageId) = insertResult.get()
if (targetMessage.expireStarted > 0) {
markExpireStarted(messageId, targetMessage.expireStarted)
}
writableDatabase.update(TABLE_NAME)
.values(LATEST_REVISION_ID to messageId)
.where("$ID_WHERE OR $LATEST_REVISION_ID = ?", targetMessage.id, targetMessage.id)
.run()
reactions.moveReactionsToNewMessage(newMessageId = messageId, previousId = targetMessage.id)
notifyConversationListeners(targetMessage.threadId)
}
return insertResult
}
fun insertProfileNameChangeMessages(recipient: Recipient, newProfileName: String, previousProfileName: String) {
writableDatabase.withinTransaction { db ->
val groupRecords = groups.getGroupsContainingMember(recipient.id, false)
@ -1890,13 +1727,13 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
.readToSingleLong(-1)
}
private fun getThreadIdFor(retrieved: IncomingMediaMessage): Long {
private fun getThreadIdFor(retrieved: IncomingMessage): Long {
return if (retrieved.groupId != null) {
val groupRecipientId = recipients.getOrInsertFromPossiblyMigratedGroupId(retrieved.groupId)
val groupRecipients = Recipient.resolved(groupRecipientId)
threads.getOrCreateThreadIdFor(groupRecipients)
} else {
val sender = Recipient.resolved(retrieved.from!!)
val sender = Recipient.resolved(retrieved.from)
threads.getOrCreateThreadIdFor(sender)
}
}
@ -2586,8 +2423,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
@JvmOverloads
@Throws(MmsException::class)
fun insertMessageInbox(
retrieved: IncomingMediaMessage,
candidateThreadId: Long,
retrieved: IncomingMessage,
candidateThreadId: Long = -1,
editedMessage: MediaMmsMessageRecord? = null,
notifyObservers: Boolean = true
): Optional<InsertResult> {
@ -2599,10 +2436,25 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
candidateThreadId
}
if (retrieved.type == MessageType.GROUP_UPDATE && retrieved.groupContext?.let { GroupV2UpdateMessageUtil.isJoinRequestCancel(it) } == true) {
val result = collapseJoinRequestEventsIfPossible(threadId, retrieved)
if (result.isPresent) {
Log.d(TAG, "[insertMessageInbox] Collapsed join request events.")
return result
}
}
val silent = MessageTypes.isGroupUpdate(type) ||
retrieved.type == MessageType.IDENTITY_DEFAULT ||
retrieved.type == MessageType.IDENTITY_VERIFIED ||
retrieved.type == MessageType.IDENTITY_UPDATE
val read = silent || retrieved.isExpirationUpdate
val contentValues = contentValuesOf(
DATE_SENT to retrieved.sentTimeMillis,
DATE_SERVER to retrieved.serverTimeMillis,
FROM_RECIPIENT_ID to retrieved.from!!.serialize(),
FROM_RECIPIENT_ID to retrieved.from.serialize(),
TO_RECIPIENT_ID to Recipient.self().id.serialize(),
TYPE to type,
MMS_MESSAGE_TYPE to PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF,
@ -2614,7 +2466,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
VIEW_ONCE to if (retrieved.isViewOnce) 1 else 0,
STORY_TYPE to retrieved.storyType.code,
PARENT_STORY_ID to if (retrieved.parentStoryId != null) retrieved.parentStoryId.serialize() else 0,
READ to if (MessageTypes.isGroupUpdate(type) || retrieved.isExpirationUpdate) 1 else 0,
READ to read.toInt(),
UNIDENTIFIED to retrieved.isUnidentified,
SERVER_GUID to retrieved.serverGuid,
LATEST_REVISION_ID to null,
@ -2655,7 +2507,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
messageRanges = retrieved.messageRanges,
contentValues = contentValues,
insertListener = null,
updateThread = retrieved.storyType === StoryType.NONE,
updateThread = retrieved.storyType === StoryType.NONE && !silent,
unarchive = true
)
@ -3596,7 +3448,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
@VisibleForTesting
fun collapseJoinRequestEventsIfPossible(threadId: Long, message: IncomingGroupUpdateMessage): Optional<InsertResult> {
fun collapseJoinRequestEventsIfPossible(threadId: Long, message: IncomingMessage): Optional<InsertResult> {
var result: InsertResult? = null
writableDatabase.withinTransaction { db ->
@ -3604,21 +3456,22 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val latestMessage = reader.getNext()
if (latestMessage != null && latestMessage.isGroupV2) {
val changeEditor: Optional<ServiceId> = message.changeEditor
val changeEditor: Optional<ServiceId> = message.groupContext?.let { GroupV2UpdateMessageUtil.getChangeEditor(it) } ?: Optional.empty()
if (changeEditor.isPresent && latestMessage.isGroupV2JoinRequest(changeEditor.get())) {
val secondLatestMessage = reader.getNext()
val id: Long
val encodedBody: String
val changeRevision: Int = message.groupContext?.let { GroupV2UpdateMessageUtil.getChangeRevision(it) } ?: -1
if (secondLatestMessage != null && secondLatestMessage.isGroupV2JoinRequest(changeEditor.get())) {
id = secondLatestMessage.id
encodedBody = MessageRecord.createNewContextWithAppendedDeleteJoinRequest(secondLatestMessage, message.changeRevision, changeEditor.get().toByteString())
encodedBody = MessageRecord.createNewContextWithAppendedDeleteJoinRequest(secondLatestMessage, changeRevision, changeEditor.get().toByteString())
deleteMessage(latestMessage.id)
} else {
id = latestMessage.id
encodedBody = MessageRecord.createNewContextWithAppendedDeleteJoinRequest(latestMessage, message.changeRevision, changeEditor.get().toByteString())
encodedBody = MessageRecord.createNewContextWithAppendedDeleteJoinRequest(latestMessage, changeRevision, changeEditor.get().toByteString())
}
db.update(TABLE_NAME)
@ -4827,56 +4680,37 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
/**
* Determines the database type bitmask for theh inbound message.
* Determines the database type bitmask for the inbound message.
*/
@Throws(MmsException::class)
private fun IncomingMediaMessage.toMessageType(): Long {
var type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.SECURE_MESSAGE_BIT
var hasSpecialType = false
if (this.isPushMessage) {
type = type or MessageTypes.PUSH_MESSAGE_BIT
}
if (this.isExpirationUpdate) {
type = type or MessageTypes.EXPIRATION_TIMER_UPDATE_BIT
}
if (this.isStoryReaction) {
type = type or MessageTypes.SPECIAL_TYPE_STORY_REACTION
hasSpecialType = true
}
private fun IncomingMessage.toMessageType(): Long {
var type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT
if (this.giftBadge != null) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_GIFT_BADGE
hasSpecialType = true
}
if (this.isPaymentsNotification) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_PAYMENTS_NOTIFICATION
hasSpecialType = true
}
type = type or when (this.type) {
MessageType.NORMAL -> 0
MessageType.EXPIRATION_UPDATE -> MessageTypes.EXPIRATION_TIMER_UPDATE_BIT
MessageType.STORY_REACTION -> MessageTypes.SPECIAL_TYPE_STORY_REACTION
MessageType.PAYMENTS_NOTIFICATION -> MessageTypes.SPECIAL_TYPE_PAYMENTS_NOTIFICATION
MessageType.ACTIVATE_PAYMENTS_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST
MessageType.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED
MessageType.CONTACT_JOINED -> MessageTypes.JOINED_TYPE
MessageType.IDENTITY_UPDATE -> MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT
MessageType.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT
MessageType.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT
MessageType.END_SESSION -> MessageTypes.END_SESSION_BIT
MessageType.GROUP_UPDATE -> {
val isOnlyGroupLeave = this.groupContext?.let { GroupV2UpdateMessageUtil.isJustAGroupLeave(it) } ?: false
if (this.isActivatePaymentsRequest) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
if (isOnlyGroupLeave) {
MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT or MessageTypes.GROUP_LEAVE_BIT
} else {
MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT
}
}
type = type or MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST
hasSpecialType = true
}
if (this.isPaymentsActivated) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED
hasSpecialType = true
}
return type

View file

@ -0,0 +1,49 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database
/**
* Describes what type of message something is. This serves as an abstraction layer over the bitmasks
* in [MessageTypes]. Currently only used for [org.thoughtcrime.securesms.mms.IncomingMessage],
* but will hopefully be used more widely in the future.
*/
enum class MessageType {
/** A typical message with no special typing */
NORMAL,
/** A mobilecoin payment */
PAYMENTS_NOTIFICATION,
/** A request to activate mobilecoin payments */
ACTIVATE_PAYMENTS_REQUEST,
/** Mobilecoin payments have been activated (in response to a [ACTIVATE_PAYMENTS_REQUEST] */
PAYMENTS_ACTIVATED,
/** An emoji reaction to a story */
STORY_REACTION,
/** The chat's expiration timer has been updated */
EXPIRATION_UPDATE,
/** A new contact has joined Signal */
CONTACT_JOINED,
/** Any update to a group */
GROUP_UPDATE,
/** A user's identity/safety number has changed */
IDENTITY_UPDATE,
/** You verified a user's identity/safety number */
IDENTITY_VERIFIED,
/** You unverified a user's identity/safety number, resetting it to the default state */
IDENTITY_DEFAULT,
/** A manual session reset. This is no longer used and is only here for handling possible inbound/sync messages. */
END_SESSION
}

View file

@ -78,11 +78,11 @@ public interface MessageTypes {
long KEY_EXCHANGE_BIT = 0x8000;
long KEY_EXCHANGE_IDENTITY_VERIFIED_BIT = 0x4000;
long KEY_EXCHANGE_IDENTITY_DEFAULT_BIT = 0x2000;
long KEY_EXCHANGE_CORRUPTED_BIT = 0x1000;
// long KEY_EXCHANGE_CORRUPTED_BIT = 0x1000;
long KEY_EXCHANGE_INVALID_VERSION_BIT = 0x800;
long KEY_EXCHANGE_BUNDLE_BIT = 0x400;
long KEY_EXCHANGE_IDENTITY_UPDATE_BIT = 0x200;
long KEY_EXCHANGE_CONTENT_FORMAT = 0x100;
// long KEY_EXCHANGE_CONTENT_FORMAT = 0x100;
// Secure Message Information
long SECURE_MESSAGE_BIT = 0x800000;
@ -236,10 +236,6 @@ public interface MessageTypes {
return (type & KEY_EXCHANGE_IDENTITY_DEFAULT_BIT) != 0;
}
static boolean isCorruptedKeyExchange(long type) {
return (type & KEY_EXCHANGE_CORRUPTED_BIT) != 0;
}
static boolean isInvalidVersionKeyExchange(long type) {
return (type & KEY_EXCHANGE_INVALID_VERSION_BIT) != 0;
}
@ -248,10 +244,6 @@ public interface MessageTypes {
return (type & KEY_EXCHANGE_BUNDLE_BIT) != 0;
}
static boolean isContentBundleKeyExchange(long type) {
return (type & KEY_EXCHANGE_CONTENT_FORMAT) != 0;
}
static boolean isIdentityUpdate(long type) {
return (type & KEY_EXCHANGE_IDENTITY_UPDATE_BIT) != 0;
}

View file

@ -39,12 +39,11 @@ import org.thoughtcrime.securesms.jobs.LeaveGroupV2Job;
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.IncomingMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupHistoryEntry;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct;
@ -801,19 +800,22 @@ public class GroupsV2StateProcessor {
mmsDatabase.markAsSent(messageId, true);
threadTable.update(threadId, false, false);
} catch (MmsException e) {
Log.w(TAG, e);
Log.w(TAG, "Failed to insert outgoing update message!", e);
}
} else {
MessageTable smsDatabase = SignalDatabase.messages();
RecipientId sender = RecipientId.from(editor.get());
IncomingTextMessage incoming = new IncomingTextMessage(sender, -1, timestamp, timestamp, timestamp, "", Optional.of(groupId), 0, false, null);
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, decryptedGroupV2Context);
Optional<MessageTable.InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
try {
MessageTable smsDatabase = SignalDatabase.messages();
RecipientId sender = RecipientId.from(editor.get());
IncomingMessage groupMessage = IncomingMessage.groupUpdate(sender, timestamp, groupId, decryptedGroupV2Context);
Optional<MessageTable.InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
if (insertResult.isPresent()) {
SignalDatabase.threads().update(insertResult.get().getThreadId(), false, false);
} else {
Log.w(TAG, "Could not insert update message");
if (insertResult.isPresent()) {
SignalDatabase.threads().update(insertResult.get().getThreadId(), false, false);
} else {
Log.w(TAG, "Could not insert update message");
}
} catch (MmsException e) {
Log.w(TAG, "Failed to insert incoming update message!", e);
}
}
}

View file

@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.crypto.SecurityEvent
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.MessageTable.InsertResult
import org.thoughtcrime.securesms.database.MessageType
import org.thoughtcrime.securesms.database.NoSuchMessageException
import org.thoughtcrime.securesms.database.PaymentTable.PublicKeyConflictException
import org.thoughtcrime.securesms.database.SignalDatabase
@ -75,7 +76,7 @@ import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.isPaymentActiv
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.isStoryReaction
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.toPointer
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.toPointersWithinLimit
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.MmsException
import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.mms.StickerSlide
@ -84,9 +85,6 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.Recipient.HiddenState
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.recipients.RecipientUtil
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.EarlyMessageCacheEntry
@ -100,7 +98,7 @@ import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.api.payments.Money
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.util.OptionalUtil.asOptional
import org.whispersystems.signalservice.api.util.Preconditions
import org.whispersystems.signalservice.internal.push.BodyRange
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
@ -143,7 +141,7 @@ object DataMessageProcessor {
var messageId: MessageId? = null
when {
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, metadata.sourceDeviceId, groupId, envelope.timestamp!!)
message.isInvalid -> handleInvalidMessage(context, senderRecipient.id, groupId, envelope.timestamp!!)
message.isEndSession -> messageId = handleEndSessionMessage(context, senderRecipient.id, envelope, metadata)
message.isExpirationUpdate -> messageId = handleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient.id, groupId, message.expireTimerDuration, receivedTime, false)
message.isStoryReaction -> messageId = handleStoryReaction(context, envelope, metadata, message, senderRecipient.id, groupId)
@ -244,13 +242,12 @@ object DataMessageProcessor {
private fun handleInvalidMessage(
context: Context,
sender: RecipientId,
senderDevice: Int,
groupId: GroupId?,
timestamp: Long
) {
log(timestamp, "Invalid message.")
val insertResult: InsertResult? = insertPlaceholder(sender, senderDevice, timestamp, groupId)
val insertResult: InsertResult? = insertPlaceholder(sender, timestamp, groupId)
if (insertResult != null) {
SignalDatabase.messages.markAsInvalidMessage(insertResult.messageId)
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.threadId))
@ -265,20 +262,17 @@ object DataMessageProcessor {
): MessageId? {
log(envelope.timestamp!!, "End session message.")
val incomingTextMessage = IncomingTextMessage(
senderRecipientId,
metadata.sourceDeviceId,
envelope.timestamp!!,
envelope.serverTimestamp!!,
System.currentTimeMillis(),
"",
Optional.empty(),
0,
metadata.sealedSender,
envelope.serverGuid
val incomingMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = System.currentTimeMillis(),
isUnidentified = metadata.sealedSender,
serverGuid = envelope.serverGuid,
type = MessageType.END_SESSION
)
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(IncomingEndSessionMessage(incomingTextMessage)).orNull()
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(incomingMessage).orNull()
return if (insertResult != null) {
ApplicationDependencies.getProtocolStore().aci().deleteAllSessions(metadata.sourceServiceId.toString())
@ -318,7 +312,7 @@ object DataMessageProcessor {
}
try {
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!! - if (sideEffect) 1 else 0,
serverTimeMillis = envelope.serverTimestamp!!,
@ -418,7 +412,7 @@ object DataMessageProcessor {
return null
}
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -582,8 +576,10 @@ object DataMessageProcessor {
isPaymentsActivated: Boolean
): MessageId? {
log(envelope.timestamp!!, "Payment activation request: $isActivatePaymentsRequest activated: $isPaymentsActivated")
Preconditions.checkArgument(isActivatePaymentsRequest || isPaymentsActivated)
try {
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -591,8 +587,7 @@ object DataMessageProcessor {
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
isUnidentified = metadata.sealedSender,
serverGuid = envelope.serverGuid,
isActivatePaymentsRequest = isActivatePaymentsRequest,
isPaymentsActivated = isPaymentsActivated
type = if (isActivatePaymentsRequest) MessageType.ACTIVATE_PAYMENTS_REQUEST else MessageType.PAYMENTS_ACTIVATED
)
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(mediaMessage, -1).orNull()
@ -638,7 +633,7 @@ object DataMessageProcessor {
true
)
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipientId,
body = uuid.toString(),
sentTimeMillis = envelope.timestamp!!,
@ -648,7 +643,7 @@ object DataMessageProcessor {
isUnidentified = metadata.sealedSender,
serverGuid = envelope.serverGuid,
isPushMessage = true,
isPaymentsNotification = true
type = MessageType.PAYMENTS_NOTIFICATION
)
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(mediaMessage, -1).orNull()
@ -750,7 +745,7 @@ object DataMessageProcessor {
val bodyRanges: BodyRangeList? = message.bodyRanges.filter { it.mentionAci == null }.toList().toBodyRangeList()
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -818,7 +813,7 @@ object DataMessageProcessor {
.build()
val insertResult: InsertResult? = try {
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -874,7 +869,7 @@ object DataMessageProcessor {
handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipient, groupId, message.expireTimerDuration, receivedTime)
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -956,20 +951,19 @@ object DataMessageProcessor {
notifyTypingStoppedFromIncomingMessage(context, senderRecipient, threadRecipient.id, metadata.sourceDeviceId)
val textMessage = IncomingTextMessage(
senderRecipient.id,
metadata.sourceDeviceId,
envelope.timestamp!!,
envelope.serverTimestamp!!,
receivedTime,
body,
Optional.ofNullable(groupId),
message.expireTimerDuration.inWholeMilliseconds,
metadata.sealedSender,
envelope.serverGuid
val textMessage = IncomingMessage(
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
receivedTimeMillis = receivedTime,
body = body,
groupId = groupId,
expiresIn = message.expireTimerDuration.inWholeMilliseconds,
isUnidentified = metadata.sealedSender,
serverGuid = envelope.serverGuid
)
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(IncomingEncryptedMessage(textMessage, body)).orNull()
val insertResult: InsertResult? = SignalDatabase.messages.insertMessageInbox(textMessage).orNull()
localMetrics?.onInsertedTextMessage()
return if (insertResult != null) {
@ -1031,20 +1025,17 @@ object DataMessageProcessor {
}
}
private fun insertPlaceholder(sender: RecipientId, senderDevice: Int, timestamp: Long, groupId: GroupId?): InsertResult? {
val textMessage = IncomingTextMessage(
sender,
senderDevice,
timestamp,
-1,
System.currentTimeMillis(),
"",
groupId.asOptional(),
0,
false,
null
private fun insertPlaceholder(sender: RecipientId, timestamp: Long, groupId: GroupId?): InsertResult? {
val textMessage = IncomingMessage(
from = sender,
sentTimeMillis = timestamp,
serverTimeMillis = -1,
receivedTimeMillis = System.currentTimeMillis(),
body = "",
groupId = groupId
)
return SignalDatabase.messages.insertMessageInbox(IncomingEncryptedMessage(textMessage, "")).orNull()
return SignalDatabase.messages.insertMessageInbox(textMessage).orNull()
}
fun getValidatedQuote(context: Context, timestamp: Long, message: DataMessage): QuoteModel? {

View file

@ -19,13 +19,11 @@ import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.war
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.groupId
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.isMediaMessage
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.toPointersWithinLimit
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.notifications.v2.ConversationId.Companion.forConversation
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.util.EarlyMessageCacheEntry
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.MessageConstraintsUtil
@ -35,7 +33,6 @@ import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.Envelope
import java.util.Optional
object EditMessageProcessor {
fun process(
@ -142,7 +139,7 @@ object EditMessageProcessor {
attachments.filter {
MediaUtil.SlideType.LONG_TEXT == MediaUtil.getSlideTypeFromContentType(it.contentType)
}
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = message.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,
@ -162,7 +159,7 @@ object EditMessageProcessor {
isPushMessage = true
)
val insertResult = SignalDatabase.messages.insertEditMessageInbox(-1, mediaMessage, targetMessage).orNull()
val insertResult = SignalDatabase.messages.insertEditMessageInbox(mediaMessage, targetMessage).orNull()
if (insertResult?.insertedAttachments != null) {
SignalDatabase.runPostSuccessfulTransaction {
val downloadJobs: List<AttachmentDownloadJob> = insertResult.insertedAttachments.mapNotNull { (_, attachmentId) ->
@ -182,21 +179,18 @@ object EditMessageProcessor {
message: DataMessage,
targetMessage: MediaMmsMessageRecord
): InsertResult? {
var textMessage = IncomingTextMessage(
senderRecipientId,
metadata.sourceDeviceId,
envelope.timestamp!!,
envelope.timestamp!!,
targetMessage.dateReceived,
message.body,
Optional.ofNullable(groupId),
targetMessage.expiresIn,
metadata.sealedSender,
envelope.serverGuid
val textMessage = IncomingMessage(
from = senderRecipientId,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.timestamp!!,
receivedTimeMillis = targetMessage.dateReceived,
body = message.body,
groupId = groupId,
expiresIn = targetMessage.expiresIn,
isUnidentified = metadata.sealedSender,
serverGuid = envelope.serverGuid
)
textMessage = IncomingEncryptedMessage(textMessage, message.body)
return SignalDatabase.messages.insertEditMessageInbox(textMessage, targetMessage).orNull()
}
}

View file

@ -37,11 +37,10 @@ import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.isMediaMessage
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.isValid
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.signedGroupChange
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.toDecryptionErrorMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.notifications.v2.ConversationId
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.util.EarlyMessageCacheEntry
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.SignalLocalMetrics
@ -295,23 +294,18 @@ open class MessageContentProcessor(private val context: Context) {
}
}
private fun insertErrorMessage(context: Context, sender: Recipient, senderDevice: Int, timestamp: Long, groupId: Optional<GroupId>, marker: (Long) -> Unit) {
val textMessage = IncomingTextMessage(
sender.id,
senderDevice,
timestamp,
-1,
System.currentTimeMillis(),
"",
groupId,
0,
false,
null
private fun insertErrorMessage(context: Context, sender: Recipient, timestamp: Long, groupId: Optional<GroupId>, marker: (Long) -> Unit) {
val textMessage = IncomingMessage(
from = sender.id,
sentTimeMillis = timestamp,
serverTimeMillis = -1,
receivedTimeMillis = System.currentTimeMillis(),
groupId = groupId.orNull()
)
SignalDatabase
.messages
.insertMessageInbox(IncomingEncryptedMessage(textMessage, ""))
.insertMessageInbox(textMessage)
.ifPresent {
marker(it.messageId)
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(it.threadId))
@ -374,21 +368,21 @@ open class MessageContentProcessor(private val context: Context) {
MessageState.INVALID_VERSION -> {
warn(timestamp, "Handling invalid version.")
insertErrorMessage(context, sender, exceptionMetadata.senderDevice, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
insertErrorMessage(context, sender, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
SignalDatabase.messages.markAsInvalidVersionKeyExchange(messageId)
}
}
MessageState.LEGACY_MESSAGE -> {
warn(timestamp, "Handling legacy message.")
insertErrorMessage(context, sender, exceptionMetadata.senderDevice, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
insertErrorMessage(context, sender, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
SignalDatabase.messages.markAsLegacyVersion(messageId)
}
}
MessageState.UNSUPPORTED_DATA_MESSAGE -> {
warn(timestamp, "Handling unsupported data message.")
insertErrorMessage(context, sender, exceptionMetadata.senderDevice, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
insertErrorMessage(context, sender, timestamp, exceptionMetadata.groupId.toOptional()) { messageId ->
SignalDatabase.messages.markAsUnsupportedProtocolVersion(messageId)
}
}

View file

@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.warn
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.groupId
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.toPointer
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.MmsException
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.stories.Stories
@ -58,7 +58,7 @@ object StoryMessageProcessor {
StoryType.withoutReplies(isTextStory = storyMessage.textAttachment != null)
}
val mediaMessage = IncomingMediaMessage(
val mediaMessage = IncomingMessage(
from = senderRecipient.id,
sentTimeMillis = envelope.timestamp!!,
serverTimeMillis = envelope.serverTimestamp!!,

View file

@ -1,145 +0,0 @@
package org.thoughtcrime.securesms.mms
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.database.model.Mention
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2
import java.util.Optional
class IncomingMediaMessage(
val from: RecipientId?,
val groupId: GroupId? = null,
val body: String? = null,
val isPushMessage: Boolean = false,
val storyType: StoryType = StoryType.NONE,
val parentStoryId: ParentStoryId? = null,
val isStoryReaction: Boolean = false,
val sentTimeMillis: Long,
val serverTimeMillis: Long,
val receivedTimeMillis: Long,
val subscriptionId: Int = -1,
val expiresIn: Long = 0,
val isExpirationUpdate: Boolean = false,
val quote: QuoteModel? = null,
val isUnidentified: Boolean = false,
val isViewOnce: Boolean = false,
val serverGuid: String? = null,
val messageRanges: BodyRangeList? = null,
attachments: List<Attachment> = emptyList(),
sharedContacts: List<Contact> = emptyList(),
linkPreviews: List<LinkPreview> = emptyList(),
mentions: List<Mention> = emptyList(),
val giftBadge: GiftBadge? = null,
val isPaymentsNotification: Boolean = false,
val isActivatePaymentsRequest: Boolean = false,
val isPaymentsActivated: Boolean = false
) {
val attachments: List<Attachment> = ArrayList(attachments)
val sharedContacts: List<Contact> = ArrayList(sharedContacts)
val linkPreviews: List<LinkPreview> = ArrayList(linkPreviews)
val mentions: List<Mention> = ArrayList(mentions)
val isGroupMessage: Boolean = groupId != null
constructor(
from: RecipientId?,
groupId: Optional<GroupId>,
body: String?,
sentTimeMillis: Long,
serverTimeMillis: Long,
receivedTimeMillis: Long,
attachments: List<Attachment>?,
subscriptionId: Int,
expiresIn: Long,
expirationUpdate: Boolean,
viewOnce: Boolean,
unidentified: Boolean,
sharedContacts: Optional<List<Contact>>,
activatePaymentsRequest: Boolean,
paymentsActivated: Boolean
) : this(
from = from,
groupId = groupId.orElse(null),
body = body,
isPushMessage = false,
sentTimeMillis = sentTimeMillis,
serverTimeMillis = serverTimeMillis,
receivedTimeMillis = receivedTimeMillis,
subscriptionId = subscriptionId,
expiresIn = expiresIn,
isExpirationUpdate = expirationUpdate,
quote = null,
isUnidentified = unidentified,
isViewOnce = viewOnce,
serverGuid = null,
attachments = attachments?.let { ArrayList<Attachment>(it) } ?: emptyList(),
sharedContacts = ArrayList<Contact>(sharedContacts.orElse(emptyList())),
isActivatePaymentsRequest = activatePaymentsRequest,
isPaymentsActivated = paymentsActivated
)
@JvmOverloads
constructor(
from: RecipientId?,
sentTimeMillis: Long,
serverTimeMillis: Long,
receivedTimeMillis: Long,
storyType: StoryType,
parentStoryId: ParentStoryId?,
isStoryReaction: Boolean,
subscriptionId: Int,
expiresIn: Long,
expirationUpdate: Boolean,
viewOnce: Boolean,
unidentified: Boolean,
body: Optional<String>,
group: Optional<SignalServiceGroupV2>,
attachments: Optional<List<SignalServiceAttachment>>,
quote: Optional<QuoteModel>,
sharedContacts: Optional<List<Contact>>,
linkPreviews: Optional<List<LinkPreview>>,
mentions: Optional<List<Mention>>,
sticker: Optional<Attachment>,
serverGuid: String?,
giftBadge: GiftBadge?,
activatePaymentsRequest: Boolean,
paymentsActivated: Boolean,
messageRanges: BodyRangeList? = null
) : this(
from = from,
groupId = if (group.isPresent) GroupId.v2(group.get().masterKey) else null,
body = body.orElse(null),
isPushMessage = true,
storyType = storyType,
parentStoryId = parentStoryId,
isStoryReaction = isStoryReaction,
sentTimeMillis = sentTimeMillis,
serverTimeMillis = serverTimeMillis,
receivedTimeMillis = receivedTimeMillis,
subscriptionId = subscriptionId,
expiresIn = expiresIn,
isExpirationUpdate = expirationUpdate,
quote = quote.orElse(null),
isUnidentified = unidentified,
isViewOnce = viewOnce,
serverGuid = serverGuid,
attachments = PointerAttachment.forPointers(attachments).apply { if (sticker.isPresent) add(sticker.get()) },
sharedContacts = sharedContacts.orElse(emptyList()),
linkPreviews = linkPreviews.orElse(emptyList()),
mentions = mentions.orElse(emptyList()),
giftBadge = giftBadge,
isActivatePaymentsRequest = activatePaymentsRequest,
isPaymentsActivated = paymentsActivated,
messageRanges = messageRanges
)
}

View file

@ -0,0 +1,114 @@
package org.thoughtcrime.securesms.mms
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.database.MessageType
import org.thoughtcrime.securesms.database.model.Mention
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.recipients.RecipientId
class IncomingMessage(
val from: RecipientId,
val groupId: GroupId? = null,
val groupContext: MessageGroupContext? = null,
val body: String? = null,
val isPushMessage: Boolean = false,
val storyType: StoryType = StoryType.NONE,
val parentStoryId: ParentStoryId? = null,
val isStoryReaction: Boolean = false,
val sentTimeMillis: Long,
val serverTimeMillis: Long,
val receivedTimeMillis: Long,
val subscriptionId: Int = -1,
val expiresIn: Long = 0,
val isExpirationUpdate: Boolean = false,
val quote: QuoteModel? = null,
val isUnidentified: Boolean = false,
val isViewOnce: Boolean = false,
val serverGuid: String? = null,
val messageRanges: BodyRangeList? = null,
attachments: List<Attachment> = emptyList(),
sharedContacts: List<Contact> = emptyList(),
linkPreviews: List<LinkPreview> = emptyList(),
mentions: List<Mention> = emptyList(),
val giftBadge: GiftBadge? = null,
val type: MessageType = MessageType.NORMAL
) {
val attachments: List<Attachment> = ArrayList(attachments)
val sharedContacts: List<Contact> = ArrayList(sharedContacts)
val linkPreviews: List<LinkPreview> = ArrayList(linkPreviews)
val mentions: List<Mention> = ArrayList(mentions)
val isGroupMessage: Boolean = groupId != null
companion object {
@JvmStatic
fun identityUpdate(from: RecipientId, sentTimestamp: Long, groupId: GroupId?): IncomingMessage {
return IncomingMessage(
from = from,
sentTimeMillis = sentTimestamp,
serverTimeMillis = -1,
receivedTimeMillis = sentTimestamp,
groupId = groupId,
type = MessageType.IDENTITY_UPDATE
)
}
@JvmStatic
fun identityVerified(from: RecipientId, sentTimestamp: Long, groupId: GroupId?): IncomingMessage {
return IncomingMessage(
from = from,
sentTimeMillis = sentTimestamp,
serverTimeMillis = -1,
receivedTimeMillis = sentTimestamp,
groupId = groupId,
type = MessageType.IDENTITY_VERIFIED
)
}
@JvmStatic
fun identityDefault(from: RecipientId, sentTimestamp: Long, groupId: GroupId?): IncomingMessage {
return IncomingMessage(
from = from,
sentTimeMillis = sentTimestamp,
serverTimeMillis = -1,
receivedTimeMillis = sentTimestamp,
groupId = groupId,
type = MessageType.IDENTITY_DEFAULT
)
}
fun contactJoined(from: RecipientId, currentTime: Long): IncomingMessage {
return IncomingMessage(
from = from,
sentTimeMillis = currentTime,
serverTimeMillis = -1,
receivedTimeMillis = currentTime,
type = MessageType.CONTACT_JOINED
)
}
@JvmStatic
fun groupUpdate(from: RecipientId, timestamp: Long, groupId: GroupId, groupContext: DecryptedGroupV2Context): IncomingMessage {
val messageGroupContext = MessageGroupContext(groupContext)
return IncomingMessage(
from = from,
sentTimeMillis = timestamp,
receivedTimeMillis = timestamp,
serverTimeMillis = timestamp,
groupId = groupId,
groupContext = messageGroupContext,
body = messageGroupContext.encodedGroupContext,
type = MessageType.GROUP_UPDATE
)
}
}
}

View file

@ -5,7 +5,7 @@ import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.MediaUtil
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
@ -60,7 +60,7 @@ object ReleaseChannel {
Optional.empty()
}
val message = IncomingMediaMessage(
val message = IncomingMessage(
from = recipientId,
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = System.currentTimeMillis(),

View file

@ -1,13 +0,0 @@
package org.thoughtcrime.securesms.sms;
public class IncomingEncryptedMessage extends IncomingTextMessage {
public IncomingEncryptedMessage(IncomingTextMessage base, String newBody) {
super(base, newBody);
}
@Override
public boolean isSecureMessage() {
return true;
}
}

View file

@ -1,17 +0,0 @@
package org.thoughtcrime.securesms.sms;
public class IncomingEndSessionMessage extends IncomingTextMessage {
public IncomingEndSessionMessage(IncomingTextMessage base) {
this(base, base.getMessageBody());
}
public IncomingEndSessionMessage(IncomingTextMessage base, String newBody) {
super(base, newBody);
}
@Override
public boolean isEndSession() {
return true;
}
}

View file

@ -1,55 +0,0 @@
package org.thoughtcrime.securesms.sms;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.Optional;
public final class IncomingGroupUpdateMessage extends IncomingTextMessage {
private final MessageGroupContext groupContext;
public IncomingGroupUpdateMessage(IncomingTextMessage base, DecryptedGroupV2Context groupV2Context) {
this(base, new MessageGroupContext(groupV2Context));
}
public IncomingGroupUpdateMessage(IncomingTextMessage base, MessageGroupContext groupContext) {
super(base, groupContext.getEncodedGroupContext());
this.groupContext = groupContext;
}
@Override
public boolean isGroup() {
return true;
}
public boolean isUpdate() {
return GroupV2UpdateMessageUtil.isUpdate(groupContext) || groupContext.requireGroupV1Properties().isUpdate();
}
public boolean isGroupV2() {
return GroupV2UpdateMessageUtil.isGroupV2(groupContext);
}
public boolean isQuit() {
return !isGroupV2() && groupContext.requireGroupV1Properties().isQuit();
}
@Override
public boolean isJustAGroupLeave() {
return GroupV2UpdateMessageUtil.isJustAGroupLeave(groupContext);
}
public boolean isCancelJoinRequest() {
return GroupV2UpdateMessageUtil.isJoinRequestCancel(groupContext);
}
public int getChangeRevision() {
return GroupV2UpdateMessageUtil.getChangeRevision(groupContext);
}
public Optional<ServiceId> getChangeEditor() {
return GroupV2UpdateMessageUtil.getChangeEditor(groupContext);
}
}

View file

@ -1,15 +0,0 @@
package org.thoughtcrime.securesms.sms;
public class IncomingIdentityDefaultMessage extends IncomingTextMessage {
public IncomingIdentityDefaultMessage(IncomingTextMessage base) {
super(base, "");
}
@Override
public boolean isIdentityDefault() {
return true;
}
}

View file

@ -1,14 +0,0 @@
package org.thoughtcrime.securesms.sms;
public class IncomingIdentityUpdateMessage extends IncomingTextMessage {
public IncomingIdentityUpdateMessage(IncomingTextMessage base) {
super(base, "");
}
@Override
public boolean isIdentityUpdate() {
return true;
}
}

View file

@ -1,15 +0,0 @@
package org.thoughtcrime.securesms.sms;
public class IncomingIdentityVerifiedMessage extends IncomingTextMessage {
public IncomingIdentityVerifiedMessage(IncomingTextMessage base) {
super(base, "");
}
@Override
public boolean isIdentityVerified() {
return true;
}
}

View file

@ -1,24 +0,0 @@
package org.thoughtcrime.securesms.sms;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.Optional;
public class IncomingJoinedMessage extends IncomingTextMessage {
public IncomingJoinedMessage(RecipientId sender) {
super(sender, 1, System.currentTimeMillis(), -1, System.currentTimeMillis(), null, Optional.empty(), 0, false, null);
}
@Override
public boolean isJoined() {
return true;
}
@Override
public boolean isSecureMessage() {
return true;
}
}

View file

@ -1,294 +0,0 @@
package org.thoughtcrime.securesms.sms;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SmsMessage;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.List;
import java.util.Optional;
public class IncomingTextMessage implements Parcelable {
public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() {
@Override
public IncomingTextMessage createFromParcel(Parcel in) {
return new IncomingTextMessage(in);
}
@Override
public IncomingTextMessage[] newArray(int size) {
return new IncomingTextMessage[size];
}
};
private static final String TAG = Log.tag(IncomingTextMessage.class);
private final String message;
private final RecipientId authorId;
private final int senderDeviceId;
private final int protocol;
private final String serviceCenterAddress;
private final boolean replyPathPresent;
private final String pseudoSubject;
private final long sentTimestampMillis;
private final long serverTimestampMillis;
private final long receivedTimestampMillis;
@Nullable private final GroupId groupId;
private final boolean push;
private final int subscriptionId;
private final long expiresInMillis;
private final boolean unidentified;
@Nullable private final String serverGuid;
public IncomingTextMessage(@NonNull RecipientId authorId, @NonNull SmsMessage message, int subscriptionId) {
this.message = message.getDisplayMessageBody();
this.authorId = authorId;
this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
this.protocol = message.getProtocolIdentifier();
this.serviceCenterAddress = message.getServiceCenterAddress();
this.replyPathPresent = message.isReplyPathPresent();
this.pseudoSubject = message.getPseudoSubject();
this.sentTimestampMillis = message.getTimestampMillis();
this.serverTimestampMillis = -1;
this.receivedTimestampMillis = System.currentTimeMillis();
this.subscriptionId = subscriptionId;
this.expiresInMillis = 0;
this.groupId = null;
this.push = false;
this.unidentified = false;
this.serverGuid = null;
}
public IncomingTextMessage(@NonNull RecipientId authorId,
int senderDeviceId,
long sentTimestampMillis,
long serverTimestampMillis,
long receivedTimestampMillis,
String encodedBody,
Optional<GroupId> groupId,
long expiresInMillis,
boolean unidentified,
String serverGuid)
{
this.message = encodedBody;
this.authorId = authorId;
this.senderDeviceId = senderDeviceId;
this.protocol = 31337;
this.serviceCenterAddress = "GCM";
this.replyPathPresent = true;
this.pseudoSubject = "";
this.sentTimestampMillis = sentTimestampMillis;
this.serverTimestampMillis = serverTimestampMillis;
this.receivedTimestampMillis = receivedTimestampMillis;
this.push = true;
this.subscriptionId = -1;
this.expiresInMillis = expiresInMillis;
this.unidentified = unidentified;
this.groupId = groupId.orElse(null);
this.serverGuid = serverGuid;
}
public IncomingTextMessage(Parcel in) {
this.message = in.readString();
this.authorId = in.readParcelable(IncomingTextMessage.class.getClassLoader());
this.senderDeviceId = in.readInt();
this.protocol = in.readInt();
this.serviceCenterAddress = in.readString();
this.replyPathPresent = (in.readInt() == 1);
this.pseudoSubject = in.readString();
this.sentTimestampMillis = in.readLong();
this.serverTimestampMillis = in.readLong();
this.receivedTimestampMillis = in.readLong();
this.groupId = GroupId.parseNullableOrThrow(in.readString());
this.push = (in.readInt() == 1);
this.subscriptionId = in.readInt();
this.expiresInMillis = in.readLong();
this.unidentified = in.readInt() == 1;
this.serverGuid = in.readString();
}
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
this.message = newBody;
this.authorId = base.getAuthorId();
this.senderDeviceId = base.getAuthorDeviceId();
this.protocol = base.getProtocol();
this.serviceCenterAddress = base.getServiceCenterAddress();
this.replyPathPresent = base.isReplyPathPresent();
this.pseudoSubject = base.getPseudoSubject();
this.sentTimestampMillis = base.getSentTimestampMillis();
this.serverTimestampMillis = base.getServerTimestampMillis();
this.receivedTimestampMillis = base.getReceivedTimestampMillis();
this.groupId = base.getGroupId();
this.push = base.isPush();
this.subscriptionId = base.getSubscriptionId();
this.expiresInMillis = base.getExpiresIn();
this.unidentified = base.isUnidentified();
this.serverGuid = base.getServerGuid();
}
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
StringBuilder body = new StringBuilder();
for (IncomingTextMessage message : fragments) {
body.append(message.getMessageBody());
}
this.message = body.toString();
this.authorId = fragments.get(0).getAuthorId();
this.senderDeviceId = fragments.get(0).getAuthorDeviceId();
this.protocol = fragments.get(0).getProtocol();
this.serviceCenterAddress = fragments.get(0).getServiceCenterAddress();
this.replyPathPresent = fragments.get(0).isReplyPathPresent();
this.pseudoSubject = fragments.get(0).getPseudoSubject();
this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis();
this.serverTimestampMillis = fragments.get(0).getServerTimestampMillis();
this.receivedTimestampMillis = fragments.get(0).getReceivedTimestampMillis();
this.groupId = fragments.get(0).getGroupId();
this.push = fragments.get(0).isPush();
this.subscriptionId = fragments.get(0).getSubscriptionId();
this.expiresInMillis = fragments.get(0).getExpiresIn();
this.unidentified = fragments.get(0).isUnidentified();
this.serverGuid = fragments.get(0).getServerGuid();
}
public int getSubscriptionId() {
return subscriptionId;
}
public long getExpiresIn() {
return expiresInMillis;
}
public long getSentTimestampMillis() {
return sentTimestampMillis;
}
public long getServerTimestampMillis() {
return serverTimestampMillis;
}
public long getReceivedTimestampMillis() {
return receivedTimestampMillis;
}
public String getPseudoSubject() {
return pseudoSubject;
}
public String getMessageBody() {
return message;
}
public RecipientId getAuthorId() {
return authorId;
}
public int getAuthorDeviceId() {
return senderDeviceId;
}
public int getProtocol() {
return protocol;
}
public String getServiceCenterAddress() {
return serviceCenterAddress;
}
public boolean isReplyPathPresent() {
return replyPathPresent;
}
public boolean isSecureMessage() {
return false;
}
public boolean isPreKeyBundle() {
return isLegacyPreKeyBundle() || isContentPreKeyBundle();
}
public boolean isLegacyPreKeyBundle() {
return false;
}
public boolean isContentPreKeyBundle() {
return false;
}
public boolean isEndSession() {
return false;
}
public boolean isPush() {
return push;
}
public @Nullable GroupId getGroupId() {
return groupId;
}
public boolean isGroup() {
return false;
}
public boolean isJoined() {
return false;
}
public boolean isIdentityUpdate() {
return false;
}
public boolean isIdentityVerified() {
return false;
}
public boolean isIdentityDefault() {
return false;
}
/**
* @return True iff the message is only a group leave of a single member.
*/
public boolean isJustAGroupLeave() {
return false;
}
public boolean isUnidentified() {
return unidentified;
}
public @Nullable String getServerGuid() {
return serverGuid;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(message);
out.writeParcelable(authorId, flags);
out.writeInt(senderDeviceId);
out.writeInt(protocol);
out.writeString(serviceCenterAddress);
out.writeInt(replyPathPresent ? 1 : 0);
out.writeString(pseudoSubject);
out.writeLong(sentTimestampMillis);
out.writeString(groupId == null ? null : groupId.toString());
out.writeInt(push ? 1 : 0);
out.writeInt(subscriptionId);
out.writeLong(expiresInMillis);
out.writeInt(unidentified ? 1 : 0);
out.writeString(serverGuid);
}
}

View file

@ -25,15 +25,12 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.mms.IncomingMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingIdentityVerifiedMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.signalservice.api.SignalSessionLock;
@ -76,12 +73,14 @@ public final class IdentityUtil {
if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) {
if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, time, null, Optional.of(groupRecord.getId()), 0, false, null);
IncomingMessage incoming = verified ? IncomingMessage.identityVerified(recipient.getId(), time, groupRecord.getId())
: IncomingMessage.identityDefault(recipient.getId(), time, groupRecord.getId());
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming);
smsDatabase.insertMessageInbox(incoming);
try {
smsDatabase.insertMessageInbox(incoming);
} catch (MmsException e) {
throw new AssertionError(e);
}
} else {
RecipientId recipientId = SignalDatabase.recipients().getOrInsertFromGroupId(groupRecord.getId());
Recipient groupRecipient = Recipient.resolved(recipientId);
@ -106,12 +105,14 @@ public final class IdentityUtil {
}
if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, time, null, Optional.empty(), 0, false, null);
IncomingMessage incoming = verified ? IncomingMessage.identityVerified(recipient.getId(), time, null)
: IncomingMessage.identityDefault(recipient.getId(), time, null);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming);
smsDatabase.insertMessageInbox(incoming);
try {
smsDatabase.insertMessageInbox(incoming);
} catch (MmsException e) {
throw new AssertionError(e);
}
} else {
OutgoingMessage outgoing;
if (verified) {
@ -144,21 +145,25 @@ public final class IdentityUtil {
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipientId) && groupRecord.isActive()) {
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, time, time, null, Optional.of(groupRecord.getId()), 0, false, null);
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
IncomingMessage groupUpdate = IncomingMessage.identityUpdate(recipientId, time, groupRecord.getId());
smsDatabase.insertMessageInbox(groupUpdate);
}
}
} catch (MmsException e) {
throw new AssertionError(e);
}
IncomingTextMessage incoming = new IncomingTextMessage(recipientId, 1, time, -1, time, null, Optional.empty(), 0, false, null);
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
try {
IncomingMessage individualUpdate = IncomingMessage.identityUpdate(recipientId, time, null);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
if (insertResult.isPresent()) {
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId()));
if (insertResult.isPresent()) {
ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId()));
}
} catch (MmsException e) {
throw new AssertionError(e);
}
}
public static void saveIdentity(String user, IdentityKey identityKey) {

View file

@ -35,8 +35,6 @@ import org.thoughtcrime.securesms.database.MessageTypes.INVALID_MESSAGE_TYPE
import org.thoughtcrime.securesms.database.MessageTypes.JOINED_TYPE
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_BIT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_BUNDLE_BIT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_CONTENT_FORMAT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_CORRUPTED_BIT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT
import org.thoughtcrime.securesms.database.MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT
@ -100,10 +98,8 @@ object MessageBitmaskColumnTransformer : ColumnTransformer {
isKeyExchangeType:${type and KEY_EXCHANGE_BIT != 0L}
isIdentityVerified:${type and KEY_EXCHANGE_IDENTITY_VERIFIED_BIT != 0L}
isIdentityDefault:${type and KEY_EXCHANGE_IDENTITY_DEFAULT_BIT != 0L}
isCorruptedKeyExchange:${type and KEY_EXCHANGE_CORRUPTED_BIT != 0L}
isInvalidVersionKeyExchange:${type and KEY_EXCHANGE_INVALID_VERSION_BIT != 0L}
isBundleKeyExchange:${type and KEY_EXCHANGE_BUNDLE_BIT != 0L}
isContentBundleKeyExchange:${type and KEY_EXCHANGE_CONTENT_FORMAT != 0L}
isIdentityUpdate:${type and KEY_EXCHANGE_IDENTITY_UPDATE_BIT != 0L}
isRateLimited:${type and MESSAGE_RATE_LIMITED_BIT != 0L}
isExpirationTimerUpdate:${type and EXPIRATION_TIMER_UPDATE_BIT != 0L}

View file

@ -1,9 +1,10 @@
package org.thoughtcrime.securesms.database
import android.content.ContentValues
import org.signal.core.util.orNull
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import java.util.Optional
import java.util.UUID
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
@ -29,17 +30,16 @@ object TestSms {
unread: Boolean = false,
threadId: Long = 1
): Long {
val message = IncomingTextMessage(
sender,
senderDeviceId,
sentTimestampMillis,
serverTimestampMillis,
receivedTimestampMillis,
encodedBody,
groupId,
expiresInMillis,
unidentified,
serverGuid
val message = IncomingMessage(
from = sender,
sentTimeMillis = sentTimestampMillis,
serverTimeMillis = serverTimestampMillis,
receivedTimeMillis = receivedTimestampMillis,
body = encodedBody,
groupId = groupId.orNull(),
expiresIn = expiresInMillis,
isUnidentified = unidentified,
serverGuid = serverGuid
)
return insert(
@ -53,23 +53,22 @@ object TestSms {
fun insert(
db: AndroidSQLiteDatabase,
message: IncomingTextMessage,
message: IncomingMessage,
type: Long = MessageTypes.BASE_INBOX_TYPE,
unread: Boolean = false,
threadId: Long = 1
): Long {
val values = ContentValues().apply {
put(MessageTable.FROM_RECIPIENT_ID, message.authorId.serialize())
put(MessageTable.FROM_DEVICE_ID, message.authorDeviceId)
put(MessageTable.TO_RECIPIENT_ID, message.authorId.serialize())
put(MessageTable.DATE_RECEIVED, message.receivedTimestampMillis)
put(MessageTable.DATE_SENT, message.sentTimestampMillis)
put(MessageTable.DATE_SERVER, message.serverTimestampMillis)
put(MessageTable.FROM_RECIPIENT_ID, message.from.serialize())
put(MessageTable.TO_RECIPIENT_ID, message.from.serialize())
put(MessageTable.DATE_RECEIVED, message.receivedTimeMillis)
put(MessageTable.DATE_SENT, message.sentTimeMillis)
put(MessageTable.DATE_SERVER, message.serverTimeMillis)
put(MessageTable.READ, if (unread) 0 else 1)
put(MessageTable.SMS_SUBSCRIPTION_ID, message.subscriptionId)
put(MessageTable.EXPIRES_IN, message.expiresIn)
put(MessageTable.UNIDENTIFIED, message.isUnidentified)
put(MessageTable.BODY, message.messageBody)
put(MessageTable.BODY, message.body)
put(MessageTable.TYPE, type)
put(MessageTable.THREAD_ID, threadId)
put(MessageTable.SERVER_GUID, message.serverGuid)