Document/clean up SendableMessage

This commit is contained in:
Max Radermacher 2026-01-26 15:09:33 -06:00 committed by GitHub
parent 408cf0c14c
commit a097061fae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 85 additions and 101 deletions

View File

@ -123,23 +123,21 @@ public final class OutgoingEditMessage: TransientOutgoingMessage {
)
}
override public func buildTranscriptSyncMessage(
override public func buildSyncTranscriptMessage(
localThread: TSContactThread,
transaction: DBWriteTransaction,
) -> OutgoingSyncMessage? {
guard let thread = thread(tx: transaction) else {
owsFailDebug("Missing thread for interaction.")
return nil
tx: DBWriteTransaction,
) throws -> OutgoingSyncMessage {
guard let thread = thread(tx: tx) else {
throw OWSAssertionError("missing thread for interaction")
}
let transcript = OutgoingEditMessageSyncTranscript(
return OutgoingEditMessageSyncTranscript(
localThread: localThread,
messageThread: thread,
message: self,
isRecipientUpdate: false,
tx: transaction,
tx: tx,
)
return transcript
}
/// This override is required to properly update the correct interaction row

View File

@ -61,7 +61,7 @@ final class OutgoingStaticMessage: TransientOutgoingMessage {
override func shouldSyncTranscript() -> Bool { false }
override func buildPlainTextData(_ thread: TSThread, transaction: DBWriteTransaction) -> Data? {
override func buildPlaintextData(inThread thread: TSThread, tx: DBWriteTransaction) throws -> Data {
return self.plaintextData
}
}

View File

@ -196,7 +196,9 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp
/**
* The data representation of this message, to be encrypted, before being sent.
*/
- (nullable NSData *)buildPlainTextData:(TSThread *)thread transaction:(DBWriteTransaction *)transaction;
- (nullable NSData *)buildPlaintextDataInThread:(TSThread *)thread
tx:(DBWriteTransaction *)transaction
error:(NSError **)error NS_SWIFT_NAME(buildPlaintextData(inThread:tx:));
/**
* Intermediate protobuf representation
@ -222,9 +224,10 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp
*/
- (BOOL)shouldSyncTranscript;
- (nullable OWSOutgoingSyncMessage *)buildTranscriptSyncMessageWithLocalThread:(TSContactThread *)localThread
- (nullable OWSOutgoingSyncMessage *)buildSyncTranscriptMessageWithLocalThread:(TSContactThread *)localThread
transaction:(DBWriteTransaction *)transaction
NS_SWIFT_NAME(buildTranscriptSyncMessage(localThread:transaction:));
error:(NSError **)error
NS_SWIFT_NAME(buildSyncTranscriptMessage(localThread:tx:));
#pragma mark - Update With... Methods

View File

@ -546,23 +546,11 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1;
return contentBuilder;
}
- (nullable NSData *)buildPlainTextData:(TSThread *)thread transaction:(DBWriteTransaction *)transaction
- (nullable NSData *)buildPlaintextDataInThread:(TSThread *)thread
tx:(DBWriteTransaction *)transaction
error:(NSError **)error
{
SSKProtoContentBuilder *_Nullable contentBuilder = [self contentBuilderWithThread:thread transaction:transaction];
if (!contentBuilder) {
OWSFailDebug(@"could not build protobuf.");
return nil;
}
[contentBuilder setPniSignatureMessage:[self buildPniSignatureMessageIfNeededWithTransaction:transaction]];
NSError *error;
NSData *_Nullable contentData = [contentBuilder buildSerializedDataAndReturnError:&error];
if (error || !contentData) {
OWSFailDebug(@"could not serialize protobuf: %@", error);
return nil;
}
return contentData;
return [self _buildPlaintextDataInThread:thread tx:transaction error:error];
}
- (BOOL)shouldSyncTranscript
@ -570,10 +558,11 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1;
return YES;
}
- (nullable OWSOutgoingSyncMessage *)buildTranscriptSyncMessageWithLocalThread:(TSContactThread *)localThread
- (nullable OWSOutgoingSyncMessage *)buildSyncTranscriptMessageWithLocalThread:(TSContactThread *)localThread
transaction:(DBWriteTransaction *)transaction
error:(NSError **)error
{
return [self _buildTranscriptSyncMessageWithLocalThread:localThread tx:transaction];
return [self _buildSyncTranscriptMessageWithLocalThread:localThread tx:transaction error:error];
}
@end

View File

@ -371,6 +371,17 @@ extension TSOutgoingMessage {
@objc
public var isStorySend: Bool { isGroupStoryReply }
@objc
func _buildPlaintextData(inThread thread: TSThread, tx: DBWriteTransaction) throws -> Data {
guard let contentBuilder = self.contentBuilder(thread: thread, transaction: tx) else {
throw OWSAssertionError("couldn't build protobuf")
}
if let pniSignatureMessage = self.buildPniSignatureMessageIfNeeded(tx: tx) {
contentBuilder.setPniSignatureMessage(pniSignatureMessage)
}
return try contentBuilder.buildSerializedData()
}
@objc
func _dataMessageBuilder(thread: TSThread, tx: DBReadTransaction) -> SSKProtoDataMessageBuilder? {
let builder = SSKProtoDataMessage.builder()
@ -535,11 +546,11 @@ extension TSOutgoingMessage {
}
@objc
func _buildTranscriptSyncMessage(localThread: TSContactThread, tx: DBWriteTransaction) -> OutgoingSyncMessage? {
func _buildSyncTranscriptMessage(localThread: TSContactThread, tx: DBWriteTransaction) throws -> OutgoingSyncMessage {
owsAssertDebug(self.shouldSyncTranscript())
guard let messageThread = self.thread(tx: tx) else {
return nil
throw OWSAssertionError("missing thread for sent message")
}
return OutgoingSentMessageTranscript(
@ -551,8 +562,7 @@ extension TSOutgoingMessage {
)
}
@objc(buildPniSignatureMessageIfNeededWithTransaction:)
func buildPniSignatureMessageIfNeeded(transaction tx: DBReadTransaction) -> SSKProtoPniSignatureMessage? {
private func buildPniSignatureMessageIfNeeded(tx: DBReadTransaction) -> SSKProtoPniSignatureMessage? {
guard recipientAddressStates?.count == 1 else {
// This is probably a group message, nothing to be alarmed about.
return nil

View File

@ -9,18 +9,8 @@ import LibSignalClient
// MARK: - Message "isXYZ" properties
extension TSOutgoingMessage {
var isTransientSKDM: Bool {
(self as? OutgoingSenderKeyDistributionMessage)?.isSentOnBehalfOfOnlineMessage ?? false
}
var isResendRequest: Bool {
self is OutgoingResendRequest
}
var isSyncMessage: Bool { self is OutgoingSyncMessage }
var canSendToLocalAddress: Bool {
return isSyncMessage ||
return self is OutgoingSyncMessage ||
self is OutgoingCallMessage ||
self is OutgoingResendRequest ||
self is OWSOutgoingResendResponse
@ -471,7 +461,7 @@ public class MessageSender {
localIdentifiers: LocalIdentifiers,
tx: DBReadTransaction,
) throws -> [SignalServiceAddress] {
if message.isSyncMessage {
if message is OutgoingSyncMessage {
return [localIdentifiers.aciAddress]
}
@ -1149,7 +1139,7 @@ public class MessageSender {
// transcript is sent.
//
// NOTE: This only applies to the 'note to self' conversation.
if message.isSyncMessage {
if message is OutgoingSyncMessage {
return
}
let thread = SSKEnvironment.shared.databaseStorageRef.read { tx in message.thread(tx: tx) }
@ -1203,9 +1193,7 @@ public class MessageSender {
let messageSend = try await databaseStorage.awaitableWrite { tx in
let localThread = TSContactThread.getOrCreateThread(withContactAddress: localIdentifiers.aciAddress, transaction: tx)
guard let transcript = message.buildTranscriptSyncMessage(localThread: localThread, transaction: tx) else {
throw OWSAssertionError("Failed to build transcript")
}
let transcript = try message.buildSyncTranscriptMessage(localThread: localThread, tx: tx)
let serializedMessage = try buildAndRecordMessage(transcript, in: localThread, tx: tx)
@ -1233,9 +1221,7 @@ public class MessageSender {
in thread: TSThread,
tx: DBWriteTransaction,
) throws -> SerializedMessage {
guard let plaintextData = message.buildPlainTextData(thread, transaction: tx) else {
throw OWSAssertionError("couldn't serialize message")
}
let plaintextData = try message.buildPlaintextData(inThread: thread, tx: tx)
let messageSendLog = SSKEnvironment.shared.messageSendLogRef
let payloadId = messageSendLog.recordPayload(plaintextData, for: message, tx: tx)
return SerializedMessage(plaintextData: plaintextData, payloadId: payloadId)
@ -1376,15 +1362,12 @@ public class MessageSender {
messageSend: OWSMessageSend,
sealedSenderParameters: SealedSenderParameters?,
) async throws -> [DeviceMessage] {
guard messageSend.message.encryptionStyle == .whisper || messageSend.message.isResendRequest else {
throw OWSAssertionError("Unexpected message type")
}
return try await buildDeviceMessages(
serviceId: messageSend.serviceId,
isSelfSend: messageSend.isSelfSend,
encryptionStyle: messageSend.message.encryptionStyle,
buildPlaintextContent: { _, _ in messageSend.plaintextContent },
isTransient: messageSend.message.isOnline || messageSend.message.isTransientSKDM,
isTransient: messageSend.message.isOnline || (messageSend.message as? OutgoingSenderKeyDistributionMessage)?.isSentOnBehalfOfOnlineMessage == true,
sealedSenderParameters: sealedSenderParameters,
)
}

View File

@ -28,7 +28,7 @@ final class SealedSenderParameters {
self.endorsement = endorsement
}
/// Indicates desired behavior on the case of decryption error.
/// Indicates desired behavior if decryption fails.
var contentHint: SealedSenderContentHint {
return message.contentHint
}

View File

@ -148,7 +148,7 @@ final class OWSOutgoingResendResponse: TransientOutgoingMessage {
override func envelopeGroupIdWithTransaction(_ transaction: DBReadTransaction) -> Data? { self.originalGroupId }
override func buildPlainTextData(_ thread: TSThread, transaction tx: DBWriteTransaction) -> Data? {
override func buildPlaintextData(inThread thread: TSThread, tx: DBWriteTransaction) throws -> Data {
owsAssertDebug(self.recipientAddresses().count == 1)
let contentBuilder: SSKProtoContentBuilder = {
@ -188,12 +188,7 @@ final class OWSOutgoingResendResponse: TransientOutgoingMessage {
}
}
do {
return try contentBuilder.buildSerializedData()
} catch {
owsFailDebug("Failed to build plaintext message: \(error)")
return nil
}
return try contentBuilder.buildSerializedData()
}
private func resentProtoBuilder(from plaintextData: Data) throws -> SSKProtoContentBuilder {

View File

@ -92,14 +92,9 @@ final class OutgoingResendRequest: TransientOutgoingMessage {
return self.failedEnvelopeGroupId
}
override func buildPlainTextData(_ thread: TSThread, transaction: DBWriteTransaction) -> Data? {
do {
let decryptionErrorMessage = try DecryptionErrorMessage(bytes: decryptionErrorData)
let plaintextContent = PlaintextContent(decryptionErrorMessage)
return plaintextContent.serialize()
} catch {
owsFailDebug("Failed to build plaintext: \(error)")
return nil
}
override func buildPlaintextData(inThread thread: TSThread, tx: DBWriteTransaction) throws -> Data {
let decryptionErrorMessage = try DecryptionErrorMessage(bytes: decryptionErrorData)
let plaintextContent = PlaintextContent(decryptionErrorMessage)
return plaintextContent.serialize()
}
}

View File

@ -15,26 +15,32 @@ protocol SendableMessage {
var timestamp: UInt64 { get }
var isSyncMessage: Bool { get }
/// If true, this message corresponds to a story and should use story=true
/// authentication and story account existence semantics.
var isStorySend: Bool { get }
/// If true, this message should set the "online" flag to indicate that it
/// should only be delivered if the recipient is currently online (e.g.,
/// typing indicator messages).
var isOnline: Bool { get }
var isTransientSKDM: Bool { get }
/// If true, this message should set the "urgent" flag (e.g., text messages
/// are urgent and receipts are not).
var isUrgent: Bool { get }
var isResendRequest: Bool { get }
var canSendToLocalAddress: Bool { get }
/// Indicates desired behavior if decryption fails.
var contentHint: SealedSenderContentHint { get }
/// Indicates how the message should be encrypted.
var encryptionStyle: EncryptionStyle { get }
func buildPlainTextData(_ thread: TSThread, transaction: DBWriteTransaction) -> Data?
// TODO: Remove the thread parameter?
/// Builds the serialized Content protobuf for this message.
func buildPlaintextData(inThread thread: TSThread, tx: DBWriteTransaction) throws -> Data
// TODO: Merge this into the return value when sending a message.
var wasSentToAnyRecipient: Bool { get }
func recipientAddresses() -> [SignalServiceAddress]
@ -57,12 +63,23 @@ protocol SendableMessage {
func update(withViewedRecipient recipientAddress: SignalServiceAddress, deviceId: DeviceId, viewedTimestamp timestamp: UInt64, tx: DBWriteTransaction)
// TODO: Add SyncTranscriptableMessage protocol for these properties.
/// Indicates that this message needs a sync transcript.
///
/// If true, `buildSyncTranscriptMessage` will be invoked.
func shouldSyncTranscript() -> Bool
/// Builds a sync transcript for this message.
///
/// Only invoked if `shouldSyncTranscript` returns true.
func buildSyncTranscriptMessage(localThread: TSContactThread, tx: DBWriteTransaction) throws -> OutgoingSyncMessage
func thread(tx: DBReadTransaction) -> TSThread?
var shouldBeSaved: Bool { get }
/// True if this message should be stored in the Message Send Log.
var shouldRecordSendLog: Bool { get }
var relatedUniqueIds: Set<String> { get }
@ -73,11 +90,6 @@ protocol SendableMessage {
var isViewOnceMessage: Bool { get }
func buildTranscriptSyncMessage(
localThread: TSContactThread,
transaction: DBWriteTransaction,
) -> OutgoingSyncMessage?
func update(withHasSyncedTranscript: Bool, transaction: DBWriteTransaction)
func allAttachments(transaction: DBReadTransaction) -> [ReferencedAttachment]

View File

@ -125,19 +125,18 @@ public class OutgoingStoryMessage: TransientOutgoingMessage {
override public func shouldSyncTranscript() -> Bool { !skipSyncTranscript }
override public func buildTranscriptSyncMessage(
override public func buildSyncTranscriptMessage(
localThread: TSContactThread,
transaction: DBWriteTransaction,
) -> OutgoingSyncMessage? {
guard let storyMessage = StoryMessage.anyFetch(uniqueId: storyMessageId, transaction: transaction) else {
owsFailDebug("Missing story message")
return nil
tx: DBWriteTransaction,
) throws -> OutgoingSyncMessage {
guard let storyMessage = StoryMessage.anyFetch(uniqueId: storyMessageId, transaction: tx) else {
throw OWSAssertionError("missing story message")
}
return OutgoingStorySentMessageTranscript(
localThread: localThread,
storyMessage: storyMessage,
transaction: transaction,
transaction: tx,
)
}

View File

@ -93,7 +93,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = 100
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
let content = try! SSKProtoContent(serializedData: messageData)
XCTAssertNil(content.pniSignatureMessage)
}
@ -110,7 +110,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = 100
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
let content = try! SSKProtoContent(serializedData: messageData)
let messagePni = content.pniSignatureMessage!.pni
@ -136,7 +136,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = Date.ows_millisecondTimestamp()
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
message.updateWithSentRecipients([otherAci], wasSentByUD: true, transaction: transaction)
@ -176,7 +176,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = Date.ows_millisecondTimestamp()
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
message.updateWithSentRecipients([otherAci], wasSentByUD: true, transaction: transaction)
@ -235,7 +235,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = Date.ows_millisecondTimestamp()
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
message.updateWithSentRecipients([otherAci], wasSentByUD: false, transaction: transaction)
@ -275,7 +275,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = Date.ows_millisecondTimestamp()
let message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
message.updateWithSentRecipients([otherAci], wasSentByUD: true, transaction: transaction)
@ -319,7 +319,7 @@ class TSOutgoingMessageTest: SSKBaseTest {
messageBuilder.timestamp = Date.ows_millisecondTimestamp()
message = messageBuilder.build(transaction: transaction)
message.anyInsert(transaction: transaction)
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
let messageData = try! message.buildPlaintextData(inThread: thread, tx: transaction)
message.updateWithSentRecipients([otherAci], wasSentByUD: true, transaction: transaction)