diff --git a/SignalServiceKit/Messages/Edit/OutgoingEditMessage.swift b/SignalServiceKit/Messages/Edit/OutgoingEditMessage.swift index 6c683a63d7..b9678b282a 100644 --- a/SignalServiceKit/Messages/Edit/OutgoingEditMessage.swift +++ b/SignalServiceKit/Messages/Edit/OutgoingEditMessage.swift @@ -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 diff --git a/SignalServiceKit/Messages/Interactions/OutgoingStaticMessage.swift b/SignalServiceKit/Messages/Interactions/OutgoingStaticMessage.swift index 21be7bdeb7..38804c049c 100644 --- a/SignalServiceKit/Messages/Interactions/OutgoingStaticMessage.swift +++ b/SignalServiceKit/Messages/Interactions/OutgoingStaticMessage.swift @@ -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 } } diff --git a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.h index 93d4fa9929..24b1532ab3 100644 --- a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.h @@ -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 diff --git a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.m index 9c815f1b0d..e0fd9cf464 100644 --- a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.m @@ -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 diff --git a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.swift b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.swift index a1f90dfde7..d90b8762cf 100644 --- a/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.swift +++ b/SignalServiceKit/Messages/Interactions/TSOutgoingMessage.swift @@ -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 diff --git a/SignalServiceKit/Messages/MessageSender.swift b/SignalServiceKit/Messages/MessageSender.swift index 6c8cab3603..f3890471a1 100644 --- a/SignalServiceKit/Messages/MessageSender.swift +++ b/SignalServiceKit/Messages/MessageSender.swift @@ -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, ) } diff --git a/SignalServiceKit/Messages/OWSMessageSend.swift b/SignalServiceKit/Messages/OWSMessageSend.swift index b1e878a2b8..bdfd75c02c 100644 --- a/SignalServiceKit/Messages/OWSMessageSend.swift +++ b/SignalServiceKit/Messages/OWSMessageSend.swift @@ -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 } diff --git a/SignalServiceKit/Messages/OWSOutgoingResendResponse.swift b/SignalServiceKit/Messages/OWSOutgoingResendResponse.swift index 64a9fc0e39..1df6684949 100644 --- a/SignalServiceKit/Messages/OWSOutgoingResendResponse.swift +++ b/SignalServiceKit/Messages/OWSOutgoingResendResponse.swift @@ -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 { diff --git a/SignalServiceKit/Messages/OutgoingResendRequest.swift b/SignalServiceKit/Messages/OutgoingResendRequest.swift index 7c7cd0ed85..352d6e587f 100644 --- a/SignalServiceKit/Messages/OutgoingResendRequest.swift +++ b/SignalServiceKit/Messages/OutgoingResendRequest.swift @@ -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() } } diff --git a/SignalServiceKit/Messages/SendableMessage.swift b/SignalServiceKit/Messages/SendableMessage.swift index 2702a9fe42..e1334dd043 100644 --- a/SignalServiceKit/Messages/SendableMessage.swift +++ b/SignalServiceKit/Messages/SendableMessage.swift @@ -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 { 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] diff --git a/SignalServiceKit/Messages/Stories/OutgoingStoryMessage.swift b/SignalServiceKit/Messages/Stories/OutgoingStoryMessage.swift index b3e98a8a0a..717a4e983b 100644 --- a/SignalServiceKit/Messages/Stories/OutgoingStoryMessage.swift +++ b/SignalServiceKit/Messages/Stories/OutgoingStoryMessage.swift @@ -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, ) } diff --git a/SignalServiceKit/tests/Messages/Interactions/TSOutgoingMessageTest.swift b/SignalServiceKit/tests/Messages/Interactions/TSOutgoingMessageTest.swift index 8690b6459c..4ca6933d0f 100644 --- a/SignalServiceKit/tests/Messages/Interactions/TSOutgoingMessageTest.swift +++ b/SignalServiceKit/tests/Messages/Interactions/TSOutgoingMessageTest.swift @@ -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)