This commit is contained in:
nielsandriesse 2020-11-12 16:02:21 +11:00
parent f706e38ef7
commit 15c6784f0f
34 changed files with 201 additions and 5914 deletions

View File

@ -9,7 +9,7 @@ public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NS
public static let maxFailureCount: UInt = 10
// MARK: Initialization
init(data: Data) {
public init(data: Data) {
self.data = data
}

View File

@ -14,7 +14,7 @@ public final class MessageSendJob : NSObject, Job, NSCoding { // NSObject/NSCodi
@objc public convenience init(message: Message, publicKey: String) { self.init(message: message, destination: .contact(publicKey: publicKey)) }
@objc public convenience init(message: Message, groupPublicKey: String) { self.init(message: message, destination: .closedGroup(groupPublicKey: groupPublicKey)) }
init(message: Message, destination: Message.Destination) {
public init(message: Message, destination: Message.Destination) {
self.message = message
self.destination = destination
}

View File

@ -14,6 +14,8 @@ public final class ClosedGroupUpdate : ControlMessage {
}
// MARK: Initialization
public override init() { super.init() }
internal init(kind: Kind) {
super.init()
self.kind = kind

View File

@ -0,0 +1,39 @@
import SessionProtocolKit
import SessionUtilitiesKit
@objc(SNNullMessage)
public final class NullMessage : ControlMessage {
// MARK: Initialization
public override init() { super.init() }
// MARK: Coding
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
public override func encode(with coder: NSCoder) {
super.encode(with: coder)
}
// MARK: Proto Conversion
public override class func fromProto(_ proto: SNProtoContent) -> NullMessage? {
guard proto.nullMessage != nil else { return nil }
return NullMessage()
}
public override func toProto() -> SNProtoContent? {
let nullMessageProto = SNProtoNullMessage.builder()
let paddingSize = UInt.random(in: 0..<512) // random(in:) uses the system's default random generator, which is cryptographically secure
let padding = Data.getSecureRandomData(ofSize: paddingSize)!
nullMessageProto.setPadding(padding)
let contentProto = SNProtoContent.builder()
do {
contentProto.setNullMessage(try nullMessageProto.build())
return try contentProto.build()
} catch {
SNLog("Couldn't construct null message proto from: \(self).")
return nil
}
}
}

View File

@ -3,9 +3,11 @@ import SessionUtilitiesKit
@objc(SNSessionRequest)
public final class SessionRequest : ControlMessage {
private var preKeyBundle: PreKeyBundle?
public var preKeyBundle: PreKeyBundle?
// MARK: Initialization
public override init() { super.init() }
internal init(preKeyBundle: PreKeyBundle) {
super.init()
self.preKeyBundle = preKeyBundle

View File

@ -29,6 +29,8 @@ public final class TypingIndicator : ControlMessage {
public override var isValid: Bool { kind != nil }
// MARK: Initialization
public override init() { super.init() }
internal init(kind: Kind) {
super.init()
self.kind = kind

View File

@ -20,7 +20,7 @@ public enum MessageSender {
}
}
internal static func send(_ message: Message, to destination: Message.Destination, using transaction: Any) -> Promise<Void> {
public static func send(_ message: Message, to destination: Message.Destination, using transaction: Any) -> Promise<Void> {
switch destination {
case .contact(_), .closedGroup(_): return sendToSnodeDestination(destination, message: message, using: transaction)
case .openGroup(_, _): return sendToOpenGroupDestination(destination, message: message, using: transaction)
@ -141,6 +141,12 @@ public enum MessageSender {
}
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
message.sentTimestamp = NSDate.millisecondTimestamp()
switch destination {
case .contact(_): preconditionFailure()
case .closedGroup(_): preconditionFailure()
case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)"
}
guard message.isValid else { return Promise(error: Error.invalidMessage) }
let (channel, server) = { () -> (UInt64, String) in
switch destination {

View File

@ -362,7 +362,6 @@
C33FDC21255A581F00E217F9 /* OWSPrimaryStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC22255A581F00E217F9 /* OWSBlockingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA68255A57F900E217F9 /* OWSBlockingManager.m */; };
C33FDC23255A581F00E217F9 /* SSKPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA69255A57F900E217F9 /* SSKPreferences.swift */; };
C33FDC24255A581F00E217F9 /* OWSMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6A255A57F900E217F9 /* OWSMessageManager.m */; };
C33FDC25255A581F00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6B255A57FA00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m */; };
C33FDC26255A581F00E217F9 /* ProtoUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */; };
C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */; };
@ -401,7 +400,6 @@
C33FDC49255A581F00E217F9 /* NSTimer+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8F255A57FD00E217F9 /* NSTimer+OWS.m */; };
C33FDC4A255A582000E217F9 /* TSYapDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */; };
C33FDC4B255A582000E217F9 /* LKSyncOpenGroupsMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA91255A57FD00E217F9 /* LKSyncOpenGroupsMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC4C255A582000E217F9 /* OWSMessageSender.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA92255A57FE00E217F9 /* OWSMessageSender.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC4E255A582000E217F9 /* Data+Streaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA94255A57FE00E217F9 /* Data+Streaming.swift */; };
C33FDC4F255A582000E217F9 /* OWSChunkedOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC50255A582000E217F9 /* OWSDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDA96255A57FE00E217F9 /* OWSDispatch.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -426,7 +424,6 @@
C33FDC63255A582000E217F9 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA9255A580000E217F9 /* Mnemonic.swift */; };
C33FDC64255A582000E217F9 /* NSObject+Casting.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAA255A580000E217F9 /* NSObject+Casting.m */; };
C33FDC65255A582000E217F9 /* OWSWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAB255A580000E217F9 /* OWSWebSocket.m */; };
C33FDC66255A582000E217F9 /* OWSMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAAC255A580000E217F9 /* OWSMessageHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC67255A582000E217F9 /* OWSDeviceProvisioner.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAAD255A580000E217F9 /* OWSDeviceProvisioner.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC68255A582000E217F9 /* OWSReceiptsForSenderMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAE255A580000E217F9 /* OWSReceiptsForSenderMessage.m */; };
C33FDC69255A582000E217F9 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAAF255A580000E217F9 /* String+Trimming.swift */; };
@ -434,7 +431,6 @@
C33FDC6B255A582000E217F9 /* OWSStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB1255A580000E217F9 /* OWSStorage.m */; };
C33FDC6C255A582000E217F9 /* TSNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB2255A580000E217F9 /* TSNetworkManager.m */; };
C33FDC6D255A582000E217F9 /* TSContactThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAB3255A580000E217F9 /* TSContactThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC6E255A582000E217F9 /* OWSMessageReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB4255A580000E217F9 /* OWSMessageReceiver.m */; };
C33FDC6F255A582000E217F9 /* TSNetworkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAB5255A580000E217F9 /* TSNetworkManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC70255A582000E217F9 /* SyncMessagesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB6255A580100E217F9 /* SyncMessagesProtocol.swift */; };
C33FDC71255A582000E217F9 /* OWSFailedMessagesJob.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAB7255A580100E217F9 /* OWSFailedMessagesJob.m */; };
@ -456,7 +452,6 @@
C33FDC83255A582000E217F9 /* OWSContact.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAC9255A580200E217F9 /* OWSContact.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDC84255A582000E217F9 /* LokiMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACA255A580200E217F9 /* LokiMessage.swift */; };
C33FDC85255A582000E217F9 /* CDSSigningCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACB255A580200E217F9 /* CDSSigningCertificate.m */; };
C33FDC86255A582000E217F9 /* OWSMessageSend.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACC255A580200E217F9 /* OWSMessageSend.swift */; };
C33FDC87255A582000E217F9 /* SSKJobRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACD255A580200E217F9 /* SSKJobRecord.m */; };
C33FDC88255A582000E217F9 /* OWSVerificationStateSyncMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDACE255A580300E217F9 /* OWSVerificationStateSyncMessage.m */; };
C33FDC89255A582000E217F9 /* OWSAttachmentDownloads.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDACF255A580300E217F9 /* OWSAttachmentDownloads.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -563,7 +558,6 @@
C33FDCF4255A582000E217F9 /* Poller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3A255A580B00E217F9 /* Poller.swift */; };
C33FDCF5255A582000E217F9 /* NSNotificationCenter+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDCF7255A582000E217F9 /* OWSProfileKeyMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3D255A580B00E217F9 /* OWSProfileKeyMessage.m */; };
C33FDCF8255A582000E217F9 /* OWSMessageSender.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3E255A580B00E217F9 /* OWSMessageSender.m */; };
C33FDCF9255A582000E217F9 /* String+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3F255A580C00E217F9 /* String+SSK.swift */; };
C33FDCFA255A582000E217F9 /* SignalIOSProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */; };
C33FDCFB255A582000E217F9 /* MIMETypeUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */; };
@ -623,7 +617,6 @@
C33FDD33255A582000E217F9 /* PhoneNumberUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB79255A581000E217F9 /* PhoneNumberUtil.m */; };
C33FDD34255A582000E217F9 /* NotificationsProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD36255A582000E217F9 /* OWSVerificationStateChangeMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB7C255A581000E217F9 /* OWSVerificationStateChangeMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD37255A582000E217F9 /* OWSMessageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB7D255A581100E217F9 /* OWSMessageManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD38255A582000E217F9 /* TSPreKeyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB7E255A581100E217F9 /* TSPreKeyManager.m */; };
C33FDD39255A582000E217F9 /* FullTextSearchFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */; };
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; };
@ -642,7 +635,6 @@
C33FDD47255A582000E217F9 /* DeviceLinkingSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8D255A581200E217F9 /* DeviceLinkingSessionDelegate.swift */; };
C33FDD48255A582000E217F9 /* OWSContact+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB8E255A581200E217F9 /* OWSContact+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; };
C33FDD4A255A582000E217F9 /* OWSMessageDecrypter.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB90255A581200E217F9 /* OWSMessageDecrypter.m */; };
C33FDD4B255A582000E217F9 /* ProtoUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB91255A581200E217F9 /* ProtoUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD4C255A582000E217F9 /* OWSDeviceProvisioningService.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB92255A581200E217F9 /* OWSDeviceProvisioningService.m */; };
C33FDD4D255A582000E217F9 /* PreKeyBundle+jsonDict.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */; };
@ -653,7 +645,6 @@
C33FDD52255A582000E217F9 /* DeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB98255A581300E217F9 /* DeviceNames.swift */; };
C33FDD53255A582000E217F9 /* OWSPrimaryStorage+keyFromIntLong.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */; };
C33FDD54255A582000E217F9 /* OWS2FAManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9A255A581300E217F9 /* OWS2FAManager.m */; };
C33FDD55255A582000E217F9 /* MessageSenderJobQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9B255A581300E217F9 /* MessageSenderJobQueue.swift */; };
C33FDD56255A582000E217F9 /* TSIncomingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD57255A582000E217F9 /* OWSCallMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB9D255A581300E217F9 /* OWSCallMessageHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD58255A582000E217F9 /* TSAttachmentPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */; };
@ -691,12 +682,10 @@
C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC2255A581700E217F9 /* SSKAsserts.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD7D255A582000E217F9 /* AnyPromise+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */; };
C33FDD7E255A582000E217F9 /* TypingIndicatorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC4255A581700E217F9 /* TypingIndicatorMessage.swift */; };
C33FDD80255A582000E217F9 /* OWSMessageReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC6255A581700E217F9 /* OWSMessageReceiver.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD82255A582000E217F9 /* OWSFingerprint.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBC8255A581700E217F9 /* OWSFingerprint.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD83255A582000E217F9 /* CreatePreKeysOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBC9255A581700E217F9 /* CreatePreKeysOperation.swift */; };
C33FDD84255A582000E217F9 /* LKGroupUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */; };
C33FDD86255A582000E217F9 /* MultiDeviceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBCC255A581800E217F9 /* MultiDeviceProtocol.swift */; };
C33FDD88255A582000E217F9 /* OWSMessageServiceParams.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD89255A582000E217F9 /* OWSFingerprintBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBCF255A581800E217F9 /* OWSFingerprintBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDD8A255A582000E217F9 /* OnionRequestAPI+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBD0255A581800E217F9 /* OnionRequestAPI+Encryption.swift */; };
@ -733,7 +722,6 @@
C33FDDA9255A582000E217F9 /* TSStorageKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDAA255A582000E217F9 /* LokiDatabaseUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF0255A581B00E217F9 /* LokiDatabaseUtilities.swift */; };
C33FDDAB255A582000E217F9 /* OWSIdentityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF1255A581B00E217F9 /* OWSIdentityManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDAC255A582000E217F9 /* MessageSender+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF2255A581B00E217F9 /* MessageSender+Promise.swift */; };
C33FDDAD255A582000E217F9 /* OWSBlockedPhoneNumbersMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF3255A581B00E217F9 /* OWSBlockedPhoneNumbersMessage.h */; };
C33FDDAE255A582000E217F9 /* DisplayNameUtilities2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */; };
C33FDDAF255A582000E217F9 /* OWSDeviceProvisioningService.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF5255A581B00E217F9 /* OWSDeviceProvisioningService.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -743,7 +731,6 @@
C33FDDB3255A582000E217F9 /* OWSError.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBF9255A581C00E217F9 /* OWSError.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB4255A582000E217F9 /* PhoneNumberUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBFA255A581C00E217F9 /* PhoneNumberUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB5255A582000E217F9 /* Storage+PublicChats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */; };
C33FDDB6255A582000E217F9 /* OWSMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFC255A581C00E217F9 /* OWSMessageHandler.m */; };
C33FDDB7255A582000E217F9 /* OWSPrimaryStorage+Calling.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */; };
C33FDDB8255A582000E217F9 /* NSSet+Functional.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDB9255A582000E217F9 /* OWSOutgoingSyncMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBFF255A581C00E217F9 /* OWSOutgoingSyncMessage.m */; };
@ -761,7 +748,6 @@
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0B255A581D00E217F9 /* OWSError.m */; };
C33FDDC6255A582000E217F9 /* TSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */; };
C33FDDC7255A582000E217F9 /* OWSProvisioningMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC0D255A581E00E217F9 /* OWSProvisioningMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDC8255A582000E217F9 /* OWSMessageDecrypter.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC0E255A581E00E217F9 /* OWSMessageDecrypter.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDC9255A582000E217F9 /* LKDeviceLinkMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC0F255A581E00E217F9 /* LKDeviceLinkMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FDDCA255A582000E217F9 /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC10255A581E00E217F9 /* ProofOfWork.swift */; };
C33FDDCB255A582000E217F9 /* TSSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDC11255A581E00E217F9 /* TSSocketManager.m */; };
@ -1156,6 +1142,9 @@
C3CA3AB4255CDAE600F4C6D4 /* japanese.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3AB3255CDAE600F4C6D4 /* japanese.txt */; };
C3CA3ABE255CDB0D00F4C6D4 /* portuguese.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3ABD255CDB0D00F4C6D4 /* portuguese.txt */; };
C3CA3AC8255CDB2900F4C6D4 /* spanish.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3AC7255CDB2900F4C6D4 /* spanish.txt */; };
C3CA3B13255CF18200F4C6D4 /* Destination+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */; };
C3CA3B1D255CF3C800F4C6D4 /* MessageSender+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */; };
C3CA3B2F255CF84E00F4C6D4 /* NullMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3CA3B2E255CF84E00F4C6D4 /* NullMessage.swift */; };
C3D0972B2510499C00F6E3E4 /* BackgroundPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */; };
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
@ -1728,7 +1717,6 @@
C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSPrimaryStorage.h; sourceTree = "<group>"; };
C33FDA68255A57F900E217F9 /* OWSBlockingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBlockingManager.m; sourceTree = "<group>"; };
C33FDA69255A57F900E217F9 /* SSKPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSKPreferences.swift; sourceTree = "<group>"; };
C33FDA6A255A57F900E217F9 /* OWSMessageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageManager.m; sourceTree = "<group>"; };
C33FDA6B255A57FA00E217F9 /* OWSDisappearingConfigurationUpdateInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisappearingConfigurationUpdateInfoMessage.m; sourceTree = "<group>"; };
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoUtils.m; sourceTree = "<group>"; };
C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "YapDatabase+Promise.swift"; sourceTree = "<group>"; };
@ -1767,7 +1755,6 @@
C33FDA8F255A57FD00E217F9 /* NSTimer+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+OWS.m"; sourceTree = "<group>"; };
C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSYapDatabaseObject.m; sourceTree = "<group>"; };
C33FDA91255A57FD00E217F9 /* LKSyncOpenGroupsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKSyncOpenGroupsMessage.h; sourceTree = "<group>"; };
C33FDA92255A57FE00E217F9 /* OWSMessageSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageSender.h; sourceTree = "<group>"; };
C33FDA94255A57FE00E217F9 /* Data+Streaming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Streaming.swift"; sourceTree = "<group>"; };
C33FDA95255A57FE00E217F9 /* OWSChunkedOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSChunkedOutputStream.h; sourceTree = "<group>"; };
C33FDA96255A57FE00E217F9 /* OWSDispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDispatch.h; sourceTree = "<group>"; };
@ -1792,7 +1779,6 @@
C33FDAA9255A580000E217F9 /* Mnemonic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = "<group>"; };
C33FDAAA255A580000E217F9 /* NSObject+Casting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+Casting.m"; sourceTree = "<group>"; };
C33FDAAB255A580000E217F9 /* OWSWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSWebSocket.m; sourceTree = "<group>"; };
C33FDAAC255A580000E217F9 /* OWSMessageHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageHandler.h; sourceTree = "<group>"; };
C33FDAAD255A580000E217F9 /* OWSDeviceProvisioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioner.h; sourceTree = "<group>"; };
C33FDAAE255A580000E217F9 /* OWSReceiptsForSenderMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSReceiptsForSenderMessage.m; sourceTree = "<group>"; };
C33FDAAF255A580000E217F9 /* String+Trimming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = "<group>"; };
@ -1800,7 +1786,6 @@
C33FDAB1255A580000E217F9 /* OWSStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSStorage.m; sourceTree = "<group>"; };
C33FDAB2255A580000E217F9 /* TSNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSNetworkManager.m; sourceTree = "<group>"; };
C33FDAB3255A580000E217F9 /* TSContactThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSContactThread.h; sourceTree = "<group>"; };
C33FDAB4255A580000E217F9 /* OWSMessageReceiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageReceiver.m; sourceTree = "<group>"; };
C33FDAB5255A580000E217F9 /* TSNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSNetworkManager.h; sourceTree = "<group>"; };
C33FDAB6255A580100E217F9 /* SyncMessagesProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncMessagesProtocol.swift; sourceTree = "<group>"; };
C33FDAB7255A580100E217F9 /* OWSFailedMessagesJob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFailedMessagesJob.m; sourceTree = "<group>"; };
@ -1822,7 +1807,6 @@
C33FDAC9255A580200E217F9 /* OWSContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContact.h; sourceTree = "<group>"; };
C33FDACA255A580200E217F9 /* LokiMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiMessage.swift; sourceTree = "<group>"; };
C33FDACB255A580200E217F9 /* CDSSigningCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDSSigningCertificate.m; sourceTree = "<group>"; };
C33FDACC255A580200E217F9 /* OWSMessageSend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSMessageSend.swift; sourceTree = "<group>"; };
C33FDACD255A580200E217F9 /* SSKJobRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKJobRecord.m; sourceTree = "<group>"; };
C33FDACE255A580300E217F9 /* OWSVerificationStateSyncMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSVerificationStateSyncMessage.m; sourceTree = "<group>"; };
C33FDACF255A580300E217F9 /* OWSAttachmentDownloads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAttachmentDownloads.h; sourceTree = "<group>"; };
@ -1929,7 +1913,6 @@
C33FDB3A255A580B00E217F9 /* Poller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poller.swift; sourceTree = "<group>"; };
C33FDB3B255A580B00E217F9 /* NSNotificationCenter+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OWS.h"; sourceTree = "<group>"; };
C33FDB3D255A580B00E217F9 /* OWSProfileKeyMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProfileKeyMessage.m; sourceTree = "<group>"; };
C33FDB3E255A580B00E217F9 /* OWSMessageSender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageSender.m; sourceTree = "<group>"; };
C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = "<group>"; };
C33FDB40255A580C00E217F9 /* SignalIOSProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalIOSProto.swift; sourceTree = "<group>"; };
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIMETypeUtil.m; sourceTree = "<group>"; };
@ -1989,7 +1972,6 @@
C33FDB79255A581000E217F9 /* PhoneNumberUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhoneNumberUtil.m; sourceTree = "<group>"; };
C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationsProtocol.h; sourceTree = "<group>"; };
C33FDB7C255A581000E217F9 /* OWSVerificationStateChangeMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSVerificationStateChangeMessage.h; sourceTree = "<group>"; };
C33FDB7D255A581100E217F9 /* OWSMessageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageManager.h; sourceTree = "<group>"; };
C33FDB7E255A581100E217F9 /* TSPreKeyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPreKeyManager.m; sourceTree = "<group>"; };
C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullTextSearchFinder.swift; sourceTree = "<group>"; };
C33FDB80255A581100E217F9 /* Notification+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Loki.swift"; sourceTree = "<group>"; };
@ -2008,7 +1990,6 @@
C33FDB8D255A581200E217F9 /* DeviceLinkingSessionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLinkingSessionDelegate.swift; sourceTree = "<group>"; };
C33FDB8E255A581200E217F9 /* OWSContact+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OWSContact+Private.h"; sourceTree = "<group>"; };
C33FDB8F255A581200E217F9 /* ParamParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParamParser.swift; sourceTree = "<group>"; };
C33FDB90255A581200E217F9 /* OWSMessageDecrypter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageDecrypter.m; sourceTree = "<group>"; };
C33FDB91255A581200E217F9 /* ProtoUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtoUtils.h; sourceTree = "<group>"; };
C33FDB92255A581200E217F9 /* OWSDeviceProvisioningService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDeviceProvisioningService.m; sourceTree = "<group>"; };
C33FDB93255A581200E217F9 /* PreKeyBundle+jsonDict.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PreKeyBundle+jsonDict.m"; sourceTree = "<group>"; };
@ -2019,7 +2000,6 @@
C33FDB98255A581300E217F9 /* DeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceNames.swift; sourceTree = "<group>"; };
C33FDB99255A581300E217F9 /* OWSPrimaryStorage+keyFromIntLong.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+keyFromIntLong.m"; sourceTree = "<group>"; };
C33FDB9A255A581300E217F9 /* OWS2FAManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWS2FAManager.m; sourceTree = "<group>"; };
C33FDB9B255A581300E217F9 /* MessageSenderJobQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSenderJobQueue.swift; sourceTree = "<group>"; };
C33FDB9C255A581300E217F9 /* TSIncomingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSIncomingMessage.h; sourceTree = "<group>"; };
C33FDB9D255A581300E217F9 /* OWSCallMessageHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCallMessageHandler.h; sourceTree = "<group>"; };
C33FDB9E255A581400E217F9 /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentPointer.m; sourceTree = "<group>"; };
@ -2057,12 +2037,10 @@
C33FDBC2255A581700E217F9 /* SSKAsserts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKAsserts.h; sourceTree = "<group>"; };
C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnyPromise+Conversion.swift"; sourceTree = "<group>"; };
C33FDBC4255A581700E217F9 /* TypingIndicatorMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorMessage.swift; sourceTree = "<group>"; };
C33FDBC6255A581700E217F9 /* OWSMessageReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageReceiver.h; sourceTree = "<group>"; };
C33FDBC8255A581700E217F9 /* OWSFingerprint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFingerprint.h; sourceTree = "<group>"; };
C33FDBC9255A581700E217F9 /* CreatePreKeysOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreatePreKeysOperation.swift; sourceTree = "<group>"; };
C33FDBCA255A581700E217F9 /* LKGroupUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKGroupUtilities.h; sourceTree = "<group>"; };
C33FDBCB255A581800E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInvalidIdentityKeyReceivingErrorMessage.m; sourceTree = "<group>"; };
C33FDBCC255A581800E217F9 /* MultiDeviceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiDeviceProtocol.swift; sourceTree = "<group>"; };
C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageServiceParams.h; sourceTree = "<group>"; };
C33FDBCF255A581800E217F9 /* OWSFingerprintBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFingerprintBuilder.h; sourceTree = "<group>"; };
C33FDBD0255A581800E217F9 /* OnionRequestAPI+Encryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OnionRequestAPI+Encryption.swift"; sourceTree = "<group>"; };
@ -2099,7 +2077,6 @@
C33FDBEF255A581B00E217F9 /* TSStorageKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSStorageKeys.h; sourceTree = "<group>"; };
C33FDBF0255A581B00E217F9 /* LokiDatabaseUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiDatabaseUtilities.swift; sourceTree = "<group>"; };
C33FDBF1255A581B00E217F9 /* OWSIdentityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIdentityManager.h; sourceTree = "<group>"; };
C33FDBF2255A581B00E217F9 /* MessageSender+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageSender+Promise.swift"; sourceTree = "<group>"; };
C33FDBF3255A581B00E217F9 /* OWSBlockedPhoneNumbersMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBlockedPhoneNumbersMessage.h; sourceTree = "<group>"; };
C33FDBF4255A581B00E217F9 /* DisplayNameUtilities2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayNameUtilities2.swift; sourceTree = "<group>"; };
C33FDBF5255A581B00E217F9 /* OWSDeviceProvisioningService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDeviceProvisioningService.h; sourceTree = "<group>"; };
@ -2109,7 +2086,6 @@
C33FDBF9255A581C00E217F9 /* OWSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSError.h; sourceTree = "<group>"; };
C33FDBFA255A581C00E217F9 /* PhoneNumberUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhoneNumberUtil.h; sourceTree = "<group>"; };
C33FDBFB255A581C00E217F9 /* Storage+PublicChats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+PublicChats.swift"; sourceTree = "<group>"; };
C33FDBFC255A581C00E217F9 /* OWSMessageHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageHandler.m; sourceTree = "<group>"; };
C33FDBFD255A581C00E217F9 /* OWSPrimaryStorage+Calling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OWSPrimaryStorage+Calling.m"; sourceTree = "<group>"; };
C33FDBFE255A581C00E217F9 /* NSSet+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+Functional.h"; sourceTree = "<group>"; };
C33FDBFF255A581C00E217F9 /* OWSOutgoingSyncMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingSyncMessage.m; sourceTree = "<group>"; };
@ -2127,7 +2103,6 @@
C33FDC0B255A581D00E217F9 /* OWSError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSError.m; sourceTree = "<group>"; };
C33FDC0C255A581E00E217F9 /* TSInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInfoMessage.m; sourceTree = "<group>"; };
C33FDC0D255A581E00E217F9 /* OWSProvisioningMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProvisioningMessage.h; sourceTree = "<group>"; };
C33FDC0E255A581E00E217F9 /* OWSMessageDecrypter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageDecrypter.h; sourceTree = "<group>"; };
C33FDC0F255A581E00E217F9 /* LKDeviceLinkMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LKDeviceLinkMessage.h; sourceTree = "<group>"; };
C33FDC10255A581E00E217F9 /* ProofOfWork.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = "<group>"; };
C33FDC11255A581E00E217F9 /* TSSocketManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSSocketManager.m; sourceTree = "<group>"; };
@ -2525,6 +2500,9 @@
C3CA3AB3255CDAE600F4C6D4 /* japanese.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = japanese.txt; sourceTree = "<group>"; };
C3CA3ABD255CDB0D00F4C6D4 /* portuguese.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = portuguese.txt; sourceTree = "<group>"; };
C3CA3AC7255CDB2900F4C6D4 /* spanish.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = spanish.txt; sourceTree = "<group>"; };
C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Destination+Conversion.swift"; sourceTree = "<group>"; };
C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+Utilities.swift"; sourceTree = "<group>"; };
C3CA3B2E255CF84E00F4C6D4 /* NullMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NullMessage.swift; sourceTree = "<group>"; };
C3D0972A2510499C00F6E3E4 /* BackgroundPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundPoller.swift; sourceTree = "<group>"; };
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
@ -3230,6 +3208,7 @@
C3C2A7702553A41E00C340D1 /* ControlMessage.swift */,
C300A5BC2554B00D00555489 /* ReadReceipt.swift */,
C300A5C82554B04E00555489 /* SessionRequest.swift */,
C3CA3B2E255CF84E00F4C6D4 /* NullMessage.swift */,
C300A5D22554B05A00555489 /* TypingIndicator.swift */,
C300A5DC2554B06600555489 /* ClosedGroupUpdate.swift */,
C300A5E62554B07300555489 /* ExpirationTimerUpdate.swift */,
@ -3350,6 +3329,7 @@
C33FD9AC255A548A00E217F9 /* SignalUtilitiesKit */ = {
isa = PBXGroup;
children = (
C3CA3B11255CF17200F4C6D4 /* Utilities */,
C33FDC09255A581D00E217F9 /* AccountServiceClient.swift */,
C33FDBC3255A581700E217F9 /* AnyPromise+Conversion.swift */,
C33FDB8A255A581200E217F9 /* AppContext.h */,
@ -3609,13 +3589,10 @@
C33FDAFD255A580600E217F9 /* LRUCache.swift */,
C33FDA7E255A57FB00E217F9 /* Mention.swift */,
C33FDA81255A57FC00E217F9 /* MentionsManager.swift */,
C33FDBF2255A581B00E217F9 /* MessageSender+Promise.swift */,
C33FDB9B255A581300E217F9 /* MessageSenderJobQueue.swift */,
C33FDB82255A581100E217F9 /* MessageWrapper.swift */,
C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */,
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */,
C33FDAA9255A580000E217F9 /* Mnemonic.swift */,
C33FDBCC255A581800E217F9 /* MultiDeviceProtocol.swift */,
C33FDA9F255A57FF00E217F9 /* NetworkManager.swift */,
C33FDB80255A581100E217F9 /* Notification+Loki.swift */,
C33FDB7A255A581000E217F9 /* NotificationsProtocol.h */,
@ -3736,17 +3713,6 @@
C33FDB67255A580F00E217F9 /* OWSMediaGalleryFinder.h */,
C33FDB71255A581000E217F9 /* OWSMediaGalleryFinder.m */,
C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */,
C33FDC0E255A581E00E217F9 /* OWSMessageDecrypter.h */,
C33FDB90255A581200E217F9 /* OWSMessageDecrypter.m */,
C33FDAAC255A580000E217F9 /* OWSMessageHandler.h */,
C33FDBFC255A581C00E217F9 /* OWSMessageHandler.m */,
C33FDB7D255A581100E217F9 /* OWSMessageManager.h */,
C33FDA6A255A57F900E217F9 /* OWSMessageManager.m */,
C33FDBC6255A581700E217F9 /* OWSMessageReceiver.h */,
C33FDAB4255A580000E217F9 /* OWSMessageReceiver.m */,
C33FDACC255A580200E217F9 /* OWSMessageSend.swift */,
C33FDA92255A57FE00E217F9 /* OWSMessageSender.h */,
C33FDB3E255A580B00E217F9 /* OWSMessageSender.m */,
C33FDBCE255A581800E217F9 /* OWSMessageServiceParams.h */,
C33FDB70255A580F00E217F9 /* OWSMessageServiceParams.m */,
C33FDAE8255A580500E217F9 /* OWSMessageUtils.h */,
@ -4364,6 +4330,15 @@
path = Mnemonic;
sourceTree = "<group>";
};
C3CA3B11255CF17200F4C6D4 /* Utilities */ = {
isa = PBXGroup;
children = (
C3CA3B12255CF18200F4C6D4 /* Destination+Conversion.swift */,
C3CA3B1C255CF3C800F4C6D4 /* MessageSender+Utilities.swift */,
);
path = Utilities;
sourceTree = "<group>";
};
C3F0A58F255C8E3D007BE2A3 /* Meta */ = {
isa = PBXGroup;
children = (
@ -4542,7 +4517,6 @@
C33FDD65255A582000E217F9 /* OWSFileSystem.h in Headers */,
C33FDCD6255A582000E217F9 /* UIImage+OWS.h in Headers */,
C38EF219255B6D3B007E1867 /* OWSConversationColor.h in Headers */,
C33FDD80255A582000E217F9 /* OWSMessageReceiver.h in Headers */,
C38EF369255B6DCC007E1867 /* ViewControllerUtils.h in Headers */,
C33FDC6D255A582000E217F9 /* TSContactThread.h in Headers */,
C33FDD7A255A582000E217F9 /* OWSRequestFactory.h in Headers */,
@ -4572,7 +4546,6 @@
C33FDCDF255A582000E217F9 /* TSDatabaseSecondaryIndexes.h in Headers */,
C33FDDBD255A582000E217F9 /* ByteParser.h in Headers */,
C33FDCBC255A582000E217F9 /* TSCall.h in Headers */,
C33FDD37255A582000E217F9 /* OWSMessageManager.h in Headers */,
C38EF2C3255B6DA6007E1867 /* OWSContactOffersInteraction.h in Headers */,
C33FDD95255A582000E217F9 /* OWSDevicesService.h in Headers */,
C38EF243255B6D67007E1867 /* UIViewController+OWS.h in Headers */,
@ -4616,7 +4589,6 @@
C33FDC42255A581F00E217F9 /* YapDatabaseTransaction+OWS.h in Headers */,
C33FDD48255A582000E217F9 /* OWSContact+Private.h in Headers */,
C33FDCA6255A582000E217F9 /* SignalRecipient.h in Headers */,
C33FDC66255A582000E217F9 /* OWSMessageHandler.h in Headers */,
C33FDDA1255A582000E217F9 /* NSTimer+OWS.h in Headers */,
C38EF277255B6D7A007E1867 /* OWSDatabaseMigration.h in Headers */,
C38EF294255B6D86007E1867 /* OWSSounds.h in Headers */,
@ -4629,7 +4601,6 @@
C33FDC83255A582000E217F9 /* OWSContact.h in Headers */,
C33FDCB2255A582000E217F9 /* OWSSyncGroupsRequestMessage.h in Headers */,
C33FDCA7255A582000E217F9 /* SSKMessageSenderJobRecord.h in Headers */,
C33FDDC8255A582000E217F9 /* OWSMessageDecrypter.h in Headers */,
C38EF262255B6D6F007E1867 /* OWSContactsManager.h in Headers */,
C33FDCC2255A582000E217F9 /* OWSProfileKeyMessage.h in Headers */,
C33FDCDE255A582000E217F9 /* OWSOutgoingSentMessageTranscript.h in Headers */,
@ -4653,7 +4624,6 @@
C33FDDB3255A582000E217F9 /* OWSError.h in Headers */,
C33FDD57255A582000E217F9 /* OWSCallMessageHandler.h in Headers */,
C33FDCC8255A582000E217F9 /* NSError+MessageSending.h in Headers */,
C33FDC4C255A582000E217F9 /* OWSMessageSender.h in Headers */,
C38EF403255B6DF7007E1867 /* ContactCellView.h in Headers */,
C33FDD6D255A582000E217F9 /* OWSOutgoingNullMessage.h in Headers */,
C33FDCA0255A582000E217F9 /* TSInteraction.h in Headers */,
@ -5649,7 +5619,6 @@
C33FDDA8255A582000E217F9 /* ClosedGroupUpdateMessage.swift in Sources */,
C33FDCF9255A582000E217F9 /* String+SSK.swift in Sources */,
C38EF389255B6DD2007E1867 /* AttachmentTextView.swift in Sources */,
C33FDDAC255A582000E217F9 /* MessageSender+Promise.swift in Sources */,
C38EF36A255B6DCC007E1867 /* NewNonContactConversationViewController.m in Sources */,
C38EF321255B6DBF007E1867 /* OWSAudioPlayer.m in Sources */,
C33FDD0A255A582000E217F9 /* OWSPrimaryStorage+PreKeyStore.m in Sources */,
@ -5664,7 +5633,6 @@
C38EF3C2255B6DE7007E1867 /* ImageEditorPaletteView.swift in Sources */,
C38EF245255B6D67007E1867 /* UIFont+OWS.m in Sources */,
C33FDD26255A582000E217F9 /* NSNotificationCenter+OWS.m in Sources */,
C33FDDB6255A582000E217F9 /* OWSMessageHandler.m in Sources */,
C38EF36F255B6DCC007E1867 /* OWSViewController.m in Sources */,
C33FDC80255A582000E217F9 /* Fingerprint.pb.swift in Sources */,
C33FDD42255A582000E217F9 /* TSAccountManager.m in Sources */,
@ -5678,7 +5646,6 @@
C33FDD5F255A582000E217F9 /* SignalServiceProfile.swift in Sources */,
C33FDD83255A582000E217F9 /* CreatePreKeysOperation.swift in Sources */,
C33FDDD5255A582000E217F9 /* OWSBackgroundTask.m in Sources */,
C33FDC6E255A582000E217F9 /* OWSMessageReceiver.m in Sources */,
C33FDC7E255A582000E217F9 /* TSAttachmentStream.m in Sources */,
C33FDC62255A582000E217F9 /* BuildConfiguration.swift in Sources */,
C38EF40A255B6DF7007E1867 /* OWSFlatButton.swift in Sources */,
@ -5753,7 +5720,6 @@
C33FDC7C255A582000E217F9 /* TSAttachment.m in Sources */,
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
C38EF3F3255B6DF7007E1867 /* ContactsViewHelper.m in Sources */,
C33FDC24255A581F00E217F9 /* OWSMessageManager.m in Sources */,
C33FDC46255A581F00E217F9 /* PublicChatPoller.swift in Sources */,
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */,
@ -5796,6 +5762,7 @@
C38EF3FC255B6DF7007E1867 /* AvatarImageView.swift in Sources */,
C33FDD13255A582000E217F9 /* OWSFailedAttachmentDownloadsJob.m in Sources */,
C38EF24D255B6D67007E1867 /* UIView+OWS.swift in Sources */,
C3CA3B13255CF18200F4C6D4 /* Destination+Conversion.swift in Sources */,
C33FDCB5255A582000E217F9 /* SessionMetaProtocol.swift in Sources */,
C33FDC91255A582000E217F9 /* OWSDeviceProvisioner.m in Sources */,
C33FDC6A255A582000E217F9 /* ProvisioningProto.swift in Sources */,
@ -5811,7 +5778,6 @@
C38EF310255B6DBF007E1867 /* DebugLogger.m in Sources */,
C38EF38B255B6DD2007E1867 /* AttachmentPrepViewController.swift in Sources */,
C33FDD98255A582000E217F9 /* LokiPushNotificationManager.swift in Sources */,
C33FDCF8255A582000E217F9 /* OWSMessageSender.m in Sources */,
C33FDD70255A582000E217F9 /* DataSource.m in Sources */,
C33FDD2F255A582000E217F9 /* AppReadiness.m in Sources */,
C33FDCEC255A582000E217F9 /* SSKIncrementingIdFinder.swift in Sources */,
@ -5824,7 +5790,6 @@
C33FDC3B255A581F00E217F9 /* MentionsManager.swift in Sources */,
C33FDC49255A581F00E217F9 /* NSTimer+OWS.m in Sources */,
C38EF260255B6D6F007E1867 /* OWSContactsManager.m in Sources */,
C33FDD55255A582000E217F9 /* MessageSenderJobQueue.swift in Sources */,
C33FDCFD255A582000E217F9 /* YapDatabaseConnection+OWS.m in Sources */,
C38EF3BC255B6DE7007E1867 /* ImageEditorPanGestureRecognizer.swift in Sources */,
C33FDC23255A581F00E217F9 /* SSKPreferences.swift in Sources */,
@ -5867,7 +5832,6 @@
C33FDD85255A582000E217F9 /* TSInvalidIdentityKeyReceivingErrorMessage.m in Sources */,
C33FDD2E255A582000E217F9 /* TSInvalidIdentityKeyErrorMessage.m in Sources */,
C33FDDBB255A582000E217F9 /* TSGroupThread.m in Sources */,
C33FDD4A255A582000E217F9 /* OWSMessageDecrypter.m in Sources */,
C38EF40C255B6DF7007E1867 /* GradientView.swift in Sources */,
C33FDD14255A582000E217F9 /* OWSUDManager.swift in Sources */,
C33FDC6B255A582000E217F9 /* OWSStorage.m in Sources */,
@ -5946,16 +5910,15 @@
C33FDCBA255A582000E217F9 /* OWSRequestBuilder.m in Sources */,
C33FDC2B255A581F00E217F9 /* OWSReadReceiptManager.m in Sources */,
C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */,
C33FDC86255A582000E217F9 /* OWSMessageSend.swift in Sources */,
C38EF312255B6DBF007E1867 /* OWSGroupAvatarBuilder.m in Sources */,
C38EF3B8255B6DE7007E1867 /* ImageEditorTextViewController.swift in Sources */,
C33FDD91255A582000E217F9 /* OWSMessageUtils.m in Sources */,
C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */,
C33FDCF7255A582000E217F9 /* OWSProfileKeyMessage.m in Sources */,
C3CA3B1D255CF3C800F4C6D4 /* MessageSender+Utilities.swift in Sources */,
C38EF31D255B6DBF007E1867 /* UIImage+OWS.swift in Sources */,
C38EF359255B6DCC007E1867 /* SheetViewController.swift in Sources */,
C38EF362255B6DCC007E1867 /* ContactShareApprovalViewController.swift in Sources */,
C33FDD86255A582000E217F9 /* MultiDeviceProtocol.swift in Sources */,
C33FDCAD255A582000E217F9 /* OWSPrimaryStorage+SessionStore.m in Sources */,
C38EF386255B6DD2007E1867 /* AttachmentApprovalInputAccessoryView.swift in Sources */,
C33FDC92255A582000E217F9 /* OWSGroupsOutputStream.m in Sources */,
@ -6037,6 +6000,7 @@
C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */,
C3A722802558C4E10043A11F /* AttachmentStream.swift in Sources */,
C300A5D32554B05A00555489 /* TypingIndicator.swift in Sources */,
C3CA3B2F255CF84E00F4C6D4 /* NullMessage.swift in Sources */,
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */,
C352A32F2557549C00338F3E /* NotifyPNServerJob.swift in Sources */,
C300A5F22554B09800555489 /* MessageSender.swift in Sources */,

View File

@ -27,7 +27,7 @@ public final class ClosedGroupPoller : NSObject {
guard !isPolling else { return }
isPolling = true
timer = Timer.scheduledTimer(withTimeInterval: ClosedGroupPoller.pollInterval, repeats: true) { [weak self] _ in
self?.poll()
let _ = self?.poll()
}
}
@ -64,7 +64,10 @@ public final class ClosedGroupPoller : NSObject {
guard let envelope = SSKProtoEnvelope.from(json) else { return }
do {
let data = try envelope.serializedData()
SSKEnvironment.shared.messageReceiver.handleReceivedEnvelopeData(data)
let job = MessageReceiveJob(data: data)
Storage.write { transaction in
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
}
} catch {
print("[Loki] Failed to deserialize envelope due to error: \(error).")
}

View File

@ -37,7 +37,6 @@ public final class ClosedGroupsProtocol : NSObject {
public static func createClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
// Prepare
var members = members
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let userPublicKey = getUserHexEncodedPublicKey()
// Generate a key pair for the group
let groupKeyPair = Curve25519.generateKeyPair()
@ -73,10 +72,12 @@ public final class ClosedGroupsProtocol : NSObject {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: groupKeyPair.privateKey, senderKeys: senderKeys, members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
promises.append(SSKEnvironment.shared.messageSender.sendPromise(message: closedGroupUpdateMessage))
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
let promise = MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
promises.append(promise)
}
// Add the group to the user's set of public keys to poll for
Storage.setClosedGroupPrivateKey(groupKeyPair.privateKey.toHexString(), for: groupPublicKey, using: transaction)
@ -92,7 +93,6 @@ public final class ClosedGroupsProtocol : NSObject {
/// - Note: The returned promise is only relevant for group leaving.
public static func update(_ groupPublicKey: String, with members: Set<String>, name: String, transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let userPublicKey = getUserHexEncodedPublicKey()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
@ -124,13 +124,14 @@ public final class ClosedGroupsProtocol : NSObject {
let promises: [Promise<Void>] = oldMembers.map { member in
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: [],
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: [],
members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
return SSKEnvironment.shared.messageSender.sendPromise(message: closedGroupUpdateMessage)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
return MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
}
when(resolved: promises).done2 { _ in seal.fulfill(()) }.catch2 { seal.reject($0) }
promise.done {
let _ = promise.done {
Storage.writeSync { transaction in
let allOldRatchets = Storage.getAllClosedGroupRatchets(for: groupPublicKey)
for (senderPublicKey, oldRatchet) in allOldRatchets {
@ -144,16 +145,17 @@ public final class ClosedGroupsProtocol : NSObject {
if isUserLeaving {
Storage.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
// Send closed group update messages to any new members using established channels
for member in newMembers {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [], members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// Send out the user's new ratchet to all members (minus the removed ones) using established channels
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
@ -162,9 +164,10 @@ public final class ClosedGroupsProtocol : NSObject {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
}
@ -177,10 +180,11 @@ public final class ClosedGroupsProtocol : NSObject {
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
}
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: newSenderKeys,
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: newSenderKeys,
members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
// Establish sessions if needed
establishSessionsIfNeeded(with: [String](newMembers), using: transaction)
// Send closed group update messages to the new members using established channels
@ -189,18 +193,20 @@ public final class ClosedGroupsProtocol : NSObject {
for member in newMembers {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
} else {
seal.fulfill(())
let allSenderKeys = Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name,
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name,
senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// Update the group
let newGroupModel = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
@ -240,10 +246,10 @@ public final class ClosedGroupsProtocol : NSObject {
// Send the request
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey))
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey))
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
// MARK: - Receiving
@ -273,7 +279,6 @@ public final class ClosedGroupsProtocol : NSObject {
}
private static func handleNewGroupMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, using transaction: YapDatabaseReadWriteTransaction) {
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
// Unwrap the message
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let name = closedGroupUpdate.name
@ -298,9 +303,10 @@ public final class ClosedGroupsProtocol : NSObject {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
for publicKey in missingSenderKeys.subtracting([ userPublicKey ]) {
@ -322,7 +328,7 @@ public final class ClosedGroupsProtocol : NSObject {
// Add the group to the user's set of public keys to poll for
Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
// Notify the PN server
LokiPushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
let _ = LokiPushNotificationManager.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
// Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
infoMessage.save(with: transaction)
@ -335,7 +341,6 @@ public final class ClosedGroupsProtocol : NSObject {
private static func handleInfoMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String,
using transaction: YapDatabaseReadWriteTransaction) {
// Unwrap the message
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let name = closedGroupUpdate.name
let senderKeys = closedGroupUpdate.senderKeys
@ -377,7 +382,7 @@ public final class ClosedGroupsProtocol : NSObject {
if wasUserRemoved {
Storage.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
// Notify the PN server
LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
let _ = LokiPushNotificationManager.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
} else {
establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
@ -386,9 +391,10 @@ public final class ClosedGroupsProtocol : NSObject {
guard member != userPublicKey else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) // This internally takes care of multi device
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
}
}
@ -406,7 +412,6 @@ public final class ClosedGroupsProtocol : NSObject {
private static func handleSenderKeyRequestMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// Prepare
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let userPublicKey = getUserHexEncodedPublicKey()
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
@ -431,9 +436,10 @@ public final class ClosedGroupsProtocol : NSObject {
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) // This internally takes care of multi device
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
let closedGroupUpdate = ClosedGroupUpdate()
closedGroupUpdate.kind = closedGroupUpdateKind
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
}
/// Invoked upon receiving a sender key from another user.

View File

@ -1,25 +0,0 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import PromiseKit
public extension MessageSender {
/**
* Wrap message sending in a Promise for easier callback chaining.
*/
func sendPromise(message: TSOutgoingMessage) -> Promise<Void> {
let promise: Promise<Void> = Promise { resolver in
self.send(message, success: { resolver.fulfill(()) }, failure: resolver.reject)
}
// Ensure sends complete before they're GC'd.
// This *should* be redundant, since we should be calling retainUntilComplete
// at all call sites where the promise isn't otherwise retained.
promise.retainUntilComplete()
return promise
}
}

View File

@ -1,256 +0,0 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
/// Durably enqueues a message for sending.
///
/// The queue's operations (`MessageSenderOperation`) uses `MessageSender` to send a message.
///
/// ## Retry behavior
///
/// Like all JobQueue's, MessageSenderJobQueue implements retry handling for operation errors.
///
/// `MessageSender` also includes it's own retry logic necessary to encapsulate business logic around
/// a user changing their Registration ID, or adding/removing devices. That is, it is sometimes *normal*
/// for MessageSender to have to resend to a recipient multiple times before it is accepted, and doesn't
/// represent a "failure" from the application standpoint.
///
/// So we have an inner non-durable retry (MessageSender) and an outer durable retry (MessageSenderJobQueue).
///
/// Both respect the `error.isRetryable` convention to be sure we don't keep retrying in some situations
/// (e.g. rate limiting)
@objc(SSKMessageSenderJobQueue)
public class MessageSenderJobQueue: NSObject, JobQueue {
@objc
public override init() {
super.init()
AppReadiness.runNowOrWhenAppWillBecomeReady {
self.setup()
}
}
@objc(addMessage:transaction:)
public func add(message: TSOutgoingMessage, transaction: YapDatabaseReadWriteTransaction) {
self.add(message: message, removeMessageAfterSending: false, transaction: transaction)
}
@objc(addMediaMessage:dataSource:contentType:sourceFilename:caption:albumMessageId:isTemporaryAttachment:)
public func add(mediaMessage: TSOutgoingMessage, dataSource: DataSource, contentType: String, sourceFilename: String?, caption: String?, albumMessageId: String?, isTemporaryAttachment: Bool) {
let attachmentInfo = OutgoingAttachmentInfo(dataSource: dataSource, contentType: contentType, sourceFilename: sourceFilename, caption: caption, albumMessageId: albumMessageId)
add(mediaMessage: mediaMessage, attachmentInfos: [attachmentInfo], isTemporaryAttachment: isTemporaryAttachment)
}
@objc(addMediaMessage:attachmentInfos:isTemporaryAttachment:)
public func add(mediaMessage: TSOutgoingMessage, attachmentInfos: [OutgoingAttachmentInfo], isTemporaryAttachment: Bool) {
OutgoingMessagePreparer.prepareAttachments(attachmentInfos,
inMessage: mediaMessage,
completionHandler: { error in
if let error = error {
Storage.writeSync { transaction in
mediaMessage.update(sendingError: error, transaction: transaction)
}
} else {
Storage.writeSync { transaction in
self.add(message: mediaMessage, removeMessageAfterSending: isTemporaryAttachment, transaction: transaction)
}
}
})
}
private func add(message: TSOutgoingMessage, removeMessageAfterSending: Bool, transaction: YapDatabaseReadWriteTransaction) {
assert(AppReadiness.isAppReady() || CurrentAppContext().isRunningTests)
let jobRecord: SSKMessageSenderJobRecord
do {
jobRecord = try SSKMessageSenderJobRecord(message: message, removeMessageAfterSending: false, label: self.jobRecordLabel)
} catch {
owsFailDebug("Failed to build job due to error: \(error).")
return
}
self.add(jobRecord: jobRecord, transaction: transaction)
}
// MARK: JobQueue
public typealias DurableOperationType = MessageSenderOperation
public static let jobRecordLabel: String = "MessageSender"
public static let maxRetries: UInt = 1 // Loki: We have our own retrying
public let requiresInternet: Bool = true
public var runningOperations: [MessageSenderOperation] = []
public var jobRecordLabel: String {
return type(of: self).jobRecordLabel
}
@objc
public func setup() {
defaultSetup()
}
public var isSetup: Bool = false
/// Used when the user clears their database to cancel any outstanding jobs.
@objc public func clearAllJobs() {
Storage.writeSync { transaction in
let statuses: [SSKJobRecordStatus] = [ .unknown, .ready, .running, .permanentlyFailed ]
var records: [SSKJobRecord] = []
statuses.forEach {
records += self.finder.allRecords(label: self.jobRecordLabel, status: $0, transaction: transaction)
}
records.forEach { $0.remove(with: transaction) }
}
}
public func didMarkAsReady(oldJobRecord: SSKMessageSenderJobRecord, transaction: YapDatabaseReadWriteTransaction) {
if let messageId = oldJobRecord.messageId, let message = TSOutgoingMessage.fetch(uniqueId: messageId, transaction: transaction) {
message.updateWithMarkingAllUnsentRecipientsAsSending(with: transaction)
}
}
public func buildOperation(jobRecord: SSKMessageSenderJobRecord, transaction: YapDatabaseReadTransaction) throws -> MessageSenderOperation {
let message: TSOutgoingMessage
if let invisibleMessage = jobRecord.invisibleMessage {
message = invisibleMessage
} else if let messageId = jobRecord.messageId, let fetchedMessage = TSOutgoingMessage.fetch(uniqueId: messageId, transaction: transaction) {
message = fetchedMessage
} else {
assert(jobRecord.messageId != nil)
throw JobError.obsolete(description: "Message no longer exists.")
}
return MessageSenderOperation(message: message, jobRecord: jobRecord)
}
var senderQueues: [String: OperationQueue] = [:]
let defaultQueue: OperationQueue = {
let operationQueue = OperationQueue()
operationQueue.name = "DefaultSendingQueue"
operationQueue.maxConcurrentOperationCount = 1
operationQueue.qualityOfService = .userInitiated
return operationQueue
}()
// We use a per-thread serial OperationQueue to ensure messages are delivered to the
// service in the order the user sent them.
public func operationQueue(jobRecord: SSKMessageSenderJobRecord) -> OperationQueue {
guard let threadId = jobRecord.threadId else {
return defaultQueue
}
guard let existingQueue = senderQueues[threadId] else {
let operationQueue = OperationQueue()
operationQueue.name = "SendingQueue:\(threadId)"
operationQueue.maxConcurrentOperationCount = 1
operationQueue.qualityOfService = .userInitiated
senderQueues[threadId] = operationQueue
return operationQueue
}
return existingQueue
}
}
public class MessageSenderOperation: OWSOperation, DurableOperation {
// MARK: DurableOperation
public let jobRecord: SSKMessageSenderJobRecord
weak public var durableOperationDelegate: MessageSenderJobQueue?
public var operation: OWSOperation {
return self
}
// MARK: Init
let message: TSOutgoingMessage
init(message: TSOutgoingMessage, jobRecord: SSKMessageSenderJobRecord) {
self.message = message
self.jobRecord = jobRecord
super.init()
}
// MARK: Dependencies
var messageSender: MessageSender {
return SSKEnvironment.shared.messageSender
}
// MARK: OWSOperation
override public func run() {
self.messageSender.send(message, success: reportSuccess, failure: reportError)
}
override public func didSucceed() {
Storage.writeSync { transaction in
self.durableOperationDelegate?.durableOperationDidSucceed(self, transaction: transaction)
if self.jobRecord.removeMessageAfterSending {
self.message.remove(with: transaction)
}
}
}
override public func didReportError(_ error: Error) {
let message = self.message
var isFailedSessionRequest = false
if message is SessionRequestMessage, let publicKey = message.thread.contactIdentifier() {
isFailedSessionRequest = (Storage.getSessionRequestSentTimestamp(for: publicKey) == message.timestamp)
}
Storage.writeSync { transaction in
if isFailedSessionRequest, let publicKey = message.thread.contactIdentifier() {
Storage.setSessionRequestSentTimestamp(for: publicKey, to: 0, using: transaction)
}
self.durableOperationDelegate?.durableOperation(self, didReportError: error, transaction: transaction)
}
}
override public func retryInterval() -> TimeInterval {
// Arbitrary backoff factor...
// With backOffFactor of 1.9
// try 1 delay: 0.00s
// try 2 delay: 0.19s
// ...
// try 5 delay: 1.30s
// ...
// try 11 delay: 61.31s
let backoffFactor = 1.9
let maxBackoff = 15 * kMinuteInterval
let seconds = 0.1 * min(maxBackoff, pow(backoffFactor, Double(self.jobRecord.failureCount)))
return seconds
}
override public func didFail(error: Error) {
let message = self.message
var isFailedSessionRequest = false
if message is SessionRequestMessage, let publicKey = message.thread.contactIdentifier() {
isFailedSessionRequest = (Storage.getSessionRequestSentTimestamp(for: publicKey) == message.timestamp)
}
Storage.writeSync { transaction in
if isFailedSessionRequest, let publicKey = message.thread.contactIdentifier() {
Storage.setSessionRequestSentTimestamp(for: publicKey, to: 0, using: transaction)
}
self.durableOperationDelegate?.durableOperation(self, didFailWithError: error, transaction: transaction)
self.message.update(sendingError: error, transaction: transaction)
if self.jobRecord.removeMessageAfterSending {
self.message.remove(with: transaction)
}
}
}
}

View File

@ -64,10 +64,6 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
#import <SignalUtilitiesKit/OWSHTTPSecurityPolicy.h>
#import <SignalUtilitiesKit/OWSIdentityManager.h>
#import <SignalUtilitiesKit/OWSIncomingSentMessageTranscript.h>
#import <SignalUtilitiesKit/OWSMessageDecrypter.h>
#import <SignalUtilitiesKit/OWSMessageManager.h>
#import <SignalUtilitiesKit/OWSMessageReceiver.h>
#import <SignalUtilitiesKit/OWSMessageSender.h>
#import <SignalUtilitiesKit/OWSMessageUtils.h>
#import <SignalUtilitiesKit/OWSNavigationController.h>
#import <SignalUtilitiesKit/OWSOperation.h>

View File

@ -1,274 +0,0 @@
import PromiseKit
// A few notes about making changes in this file:
//
// Don't use a database transaction if you can avoid it.
// If you do need to use a database transaction, use a read transaction if possible.
// For write transactions, consider making it the caller's responsibility to manage the database transaction (this helps avoid unnecessary transactions).
// Think carefully about adding a function; there might already be one for what you need.
// Document the expected cases in which a function will be used
// Express those cases in tests.
@objc(LKMultiDeviceProtocol)
public final class MultiDeviceProtocol : NSObject {
/// A mapping from hex encoded public key to date updated.
///
/// - Note: Should only be accessed from `LokiAPI.workQueue` to avoid race conditions.
public static var lastDeviceLinkUpdate: [String:Date] = [:]
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
// MARK: Settings
public static let deviceLinkUpdateInterval: TimeInterval = 60
// MARK: Multi Device Destination
public struct MultiDeviceDestination : Hashable {
public let publicKey: String
public let isMaster: Bool
}
// MARK: - General
@objc(isUnlinkDeviceMessage:)
public static func isUnlinkDeviceMessage(_ dataMessage: SSKProtoDataMessage) -> Bool {
let unlinkDeviceFlag = SSKProtoDataMessage.SSKProtoDataMessageFlags.unlinkDevice
return dataMessage.flags & UInt32(unlinkDeviceFlag.rawValue) != 0
}
public static func getUserLinkedDevices() -> Set<String> {
var result: Set<String> = []
storage.dbReadConnection.read { transaction in
result = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: getUserHexEncodedPublicKey(), in: transaction)
}
return result
}
@objc public static func isSlaveThread(_ thread: TSThread) -> Bool {
guard let thread = thread as? TSContactThread else { return false }
var isSlaveThread = false
storage.dbReadConnection.read { transaction in
isSlaveThread = storage.getMasterHexEncodedPublicKey(for: thread.contactIdentifier(), in: transaction) != nil
}
return isSlaveThread
}
// MARK: - Sending (Part 1)
@objc(isMultiDeviceRequiredForMessage:toPublicKey:)
public static func isMultiDeviceRequired(for message: TSOutgoingMessage, to publicKey: String) -> Bool {
return !(message is DeviceLinkMessage) && !(message is UnlinkDeviceMessage) && (message.thread as? TSGroupThread)?.groupModel.groupType != .openGroup
&& !Storage.getUserClosedGroupPublicKeys().contains(publicKey)
}
private static func copy(_ messageSend: OWSMessageSend, for destination: MultiDeviceDestination, with seal: Resolver<Void>) -> OWSMessageSend {
var recipient: SignalRecipient!
storage.dbReadConnection.read { transaction in
recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.publicKey, transaction: transaction)
}
// TODO: Why is it okay that the thread, sender certificate, etc. don't get changed?
return OWSMessageSend(message: messageSend.message, thread: messageSend.thread, recipient: recipient,
senderCertificate: messageSend.senderCertificate, udAccess: messageSend.udAccess, localNumber: messageSend.localNumber, success: {
seal.fulfill(())
}, failure: { error in
seal.reject(error)
})
}
private static func sendMessage(_ messageSend: OWSMessageSend, to destination: MultiDeviceDestination, in transaction: YapDatabaseReadTransaction) -> Promise<Void> {
let (threadPromise, threadPromiseSeal) = Promise<TSThread>.pending()
if messageSend.message.thread.isGroupThread() {
threadPromiseSeal.fulfill(messageSend.message.thread)
} else if let thread = TSContactThread.getWithContactId(destination.publicKey, transaction: transaction) {
threadPromiseSeal.fulfill(thread)
} else {
Storage.write { transaction in
let thread = TSContactThread.getOrCreateThread(withContactId: destination.publicKey, transaction: transaction)
threadPromiseSeal.fulfill(thread)
}
}
return threadPromise.then2 { thread -> Promise<Void> in
let message = messageSend.message
let messageSender = SSKEnvironment.shared.messageSender
let (promise, seal) = Promise<Void>.pending()
let messageSendCopy = copy(messageSend, for: destination, with: seal)
OWSDispatch.sendingQueue().async {
messageSender.sendMessage(messageSendCopy)
}
return promise
}
}
/// See [Multi Device Message Sending](https://github.com/loki-project/session-protocol-docs/wiki/Multi-Device-Message-Sending) for more information.
@objc(sendMessageToDestinationAndLinkedDevices:transaction:)
public static func sendMessageToDestinationAndLinkedDevices(_ messageSend: OWSMessageSend, in transaction: YapDatabaseReadTransaction) {
// if !messageSend.isUDSend && messageSend.recipient.recipientId() != getUserHexEncodedPublicKey() {
// #if DEBUG
// preconditionFailure()
// #endif
// }
let message = messageSend.message
let messageSender = SSKEnvironment.shared.messageSender
if !isMultiDeviceRequired(for: message, to: messageSend.recipient.recipientId()) {
print("[Loki] sendMessageToDestinationAndLinkedDevices(_:in:) invoked for a message that doesn't require multi device routing.")
OWSDispatch.sendingQueue().async {
messageSender.sendMessage(messageSend)
}
return
}
print("[Loki] Sending \(type(of: message)) message using multi device routing.")
let publicKey = messageSend.recipient.recipientId()
getMultiDeviceDestinations(for: publicKey, in: transaction).done2 { destinations in
var promises: [Promise<Void>] = []
let masterDestination = destinations.first { $0.isMaster }
if let masterDestination = masterDestination {
storage.dbReadConnection.read { transaction in
promises.append(sendMessage(messageSend, to: masterDestination, in: transaction))
}
}
let slaveDestinations = destinations.filter { !$0.isMaster }
slaveDestinations.forEach { slaveDestination in
storage.dbReadConnection.read { transaction in
promises.append(sendMessage(messageSend, to: slaveDestination, in: transaction))
}
}
when(resolved: promises).done(on: OWSDispatch.sendingQueue()) { results in
let errors = results.compactMap { result -> Error? in
if case PromiseKit.Result.rejected(let error) = result {
return error
} else {
return nil
}
}
if errors.isEmpty {
messageSend.success()
} else {
messageSend.failure(errors.first!)
}
}
}.catch2 { error in
// Proceed even if updating the recipient's device links failed, so that message sending
// is independent of whether the file server is online
OWSDispatch.sendingQueue().async {
messageSender.sendMessage(messageSend)
}
}
}
@objc(updateDeviceLinksIfNeededForPublicKey:transaction:)
public static func updateDeviceLinksIfNeeded(for publicKey: String, in transaction: YapDatabaseReadTransaction) -> AnyPromise {
return AnyPromise.from(getMultiDeviceDestinations(for: publicKey, in: transaction))
}
// MARK: - Receiving
@objc(handleDeviceLinkMessageIfNeeded:wrappedIn:transaction:)
public static func handleDeviceLinkMessageIfNeeded(_ protoContent: SSKProtoContent, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) {
let publicKey = envelope.source! // Set during UD decryption
guard let deviceLinkMessage = protoContent.lokiDeviceLinkMessage, let master = deviceLinkMessage.masterPublicKey,
let slave = deviceLinkMessage.slavePublicKey, let slaveSignature = deviceLinkMessage.slaveSignature else {
return print("[Loki] Received an invalid device link message.")
}
let deviceLinkingSession = DeviceLinkingSession.current
if let masterSignature = deviceLinkMessage.masterSignature { // Authorization
print("[Loki] Received a device link authorization from: \(publicKey).") // Intentionally not `master`
if let deviceLinkingSession = deviceLinkingSession {
deviceLinkingSession.processLinkingAuthorization(from: master, for: slave, masterSignature: masterSignature, slaveSignature: slaveSignature)
} else {
print("[Loki] Received a device link authorization without a session; ignoring.")
}
// Set any profile info (the device link authorization also includes the master device's profile info)
if let dataMessage = protoContent.dataMessage {
SessionMetaProtocol.updateDisplayNameIfNeeded(for: master, using: dataMessage, in: transaction)
SessionMetaProtocol.updateProfileKeyIfNeeded(for: master, using: dataMessage)
}
} else { // Request
print("[Loki] Received a device link request from: \(publicKey).") // Intentionally not `slave`
if let deviceLinkingSession = deviceLinkingSession {
deviceLinkingSession.processLinkingRequest(from: slave, to: master, with: slaveSignature)
} else {
NotificationCenter.default.post(name: .unexpectedDeviceLinkRequestReceived, object: nil)
}
}
}
@objc(handleUnlinkDeviceMessage:wrappedIn:transaction:)
public static func handleUnlinkDeviceMessage(_ dataMessage: SSKProtoDataMessage, wrappedIn envelope: SSKProtoEnvelope, using transaction: YapDatabaseReadWriteTransaction) {
let publicKey = envelope.source! // Set during UD decryption
// Check that the request was sent by our master device
let userPublicKey = getUserHexEncodedPublicKey()
guard let userMasterPublicKey = storage.getMasterHexEncodedPublicKey(for: userPublicKey, in: transaction) else { return }
let wasSentByMasterDevice = (userMasterPublicKey == publicKey)
guard wasSentByMasterDevice else { return }
// Ignore the request if we don't know about the device link in question
let masterDeviceLinks = storage.getDeviceLinks(for: userMasterPublicKey, in: transaction)
if !masterDeviceLinks.contains(where: {
$0.master.publicKey == userMasterPublicKey && $0.slave.publicKey == userPublicKey
}) {
return
}
FileServerAPI.getDeviceLinks(associatedWith: userPublicKey).done2 { slaveDeviceLinks in
// Check that the device link IS present on the file server.
// Note that the device link as seen from the master device's perspective has been deleted at this point, but the
// device link as seen from the slave perspective hasn't.
if slaveDeviceLinks.contains(where: {
$0.master.publicKey == userMasterPublicKey && $0.slave.publicKey == userPublicKey
}) {
for deviceLink in slaveDeviceLinks { // In theory there should only be one
FileServerAPI.removeDeviceLink(deviceLink) // Attempt to clean up on the file server
}
UserDefaults.standard[.wasUnlinked] = true
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataNukeRequested, object: nil)
}
}
}
}
}
// MARK: - Sending (Part 2)
// Here (in a non-@objc extension) because it doesn't interoperate well with Obj-C
public extension MultiDeviceProtocol {
fileprivate static func getMultiDeviceDestinations(for publicKey: String, in transaction: YapDatabaseReadTransaction) -> Promise<Set<MultiDeviceDestination>> {
let (promise, seal) = Promise<Set<MultiDeviceDestination>>.pending()
func getDestinations(in transaction: YapDatabaseReadTransaction? = nil) {
storage.dbReadConnection.read { transaction in
var destinations: Set<MultiDeviceDestination> = []
let masterPublicKey = storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) ?? publicKey
let masterDestination = MultiDeviceDestination(publicKey: masterPublicKey, isMaster: true)
destinations.insert(masterDestination)
let deviceLinks = storage.getDeviceLinks(for: masterPublicKey, in: transaction)
let slaveDestinations = deviceLinks.map { MultiDeviceDestination(publicKey: $0.slave.publicKey, isMaster: false) }
destinations.formUnion(slaveDestinations)
seal.fulfill(destinations)
}
}
let timeSinceLastUpdate: TimeInterval
if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[publicKey] {
timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate)
} else {
timeSinceLastUpdate = .infinity
}
if timeSinceLastUpdate > deviceLinkUpdateInterval {
let masterPublicKey = storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) ?? publicKey
FileServerAPI.getDeviceLinks(associatedWith: masterPublicKey).done2 { _ in
getDestinations()
lastDeviceLinkUpdate[publicKey] = Date()
}.catch2 { error in
if (error as? DotNetAPI.Error) == DotNetAPI.Error.parsingFailed {
// Don't immediately re-fetch in case of failure due to a parsing error
lastDeviceLinkUpdate[publicKey] = Date()
getDestinations()
} else {
print("[Loki] Failed to get device links due to error: \(error).")
seal.reject(error)
}
}
} else {
getDestinations()
}
return promise
}
}

View File

@ -1,47 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/OWSMessageHandler.h>
NS_ASSUME_NONNULL_BEGIN
@class OWSPrimaryStorage;
@class SSKProtoEnvelope;
@class YapDatabaseReadWriteTransaction;
@interface OWSMessageDecryptResult : NSObject
@property (nonatomic, readonly) NSData *envelopeData;
@property (nonatomic, readonly, nullable) NSData *plaintextData;
@property (nonatomic, readonly) NSString *source;
@property (nonatomic, readonly) UInt32 sourceDevice;
@property (nonatomic, readonly) BOOL isUDMessage;
@end
#pragma mark -
// Decryption result includes the envelope since the envelope
// may be altered by the decryption process.
typedef void (^DecryptSuccessBlock)(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction);
typedef void (^DecryptFailureBlock)(void);
@interface OWSMessageDecrypter : OWSMessageHandler
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
// decryptEnvelope: can be called from any thread.
// successBlock & failureBlock will be called an arbitrary thread.
//
// Exactly one of successBlock & failureBlock will be called,
// once.
- (void)decryptEnvelope:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeData
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(DecryptFailureBlock)failureBlock;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,686 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSMessageDecrypter.h"
#import "NSData+messagePadding.h"
#import "NSString+SSK.h"
#import "NotificationsProtocol.h"
#import "OWSAnalytics.h"
#import "OWSBlockingManager.h"
#import "OWSDevice.h"
#import "OWSError.h"
#import "OWSIdentityManager.h"
#import "OWSPrimaryStorage+PreKeyStore.h"
#import "OWSPrimaryStorage+SessionStore.h"
#import "OWSPrimaryStorage+SignedPreKeyStore.h"
#import "OWSPrimaryStorage.h"
#import "SSKEnvironment.h"
#import "SignalRecipient.h"
#import "TSAccountManager.h"
#import "TSContactThread.h"
#import "TSErrorMessage.h"
#import "TSPreKeyManager.h"
#import <SessionProtocolKit/AxolotlExceptions.h>
#import <SessionProtocolKit/NSData+keyVersionByte.h>
#import <SessionProtocolKit/SessionCipher.h>
#import <SignalCoreKit/NSData+OWS.h>
#import <SignalCoreKit/Randomness.h>
#import <SignalCoreKit/SCKExceptionWrapper.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import "SSKAsserts.h"
NS_ASSUME_NONNULL_BEGIN
NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDescription)
{
if (error) {
return error;
}
OWSCFailDebug(@"Caller should provide specific error");
return OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, fallbackErrorDescription);
}
#pragma mark -
@interface OWSMessageDecryptResult ()
@property (nonatomic) NSData *envelopeData;
@property (nonatomic, nullable) NSData *plaintextData;
@property (nonatomic) NSString *source;
@property (nonatomic) UInt32 sourceDevice;
@property (nonatomic) BOOL isUDMessage;
@end
#pragma mark -
@implementation OWSMessageDecryptResult
+ (OWSMessageDecryptResult *)resultWithEnvelopeData:(NSData *)envelopeData
plaintextData:(nullable NSData *)plaintextData
source:(NSString *)source
sourceDevice:(UInt32)sourceDevice
isUDMessage:(BOOL)isUDMessage
{
OWSAssertDebug(envelopeData);
OWSAssertDebug(source.length > 0);
OWSAssertDebug(sourceDevice > 0);
OWSMessageDecryptResult *result = [OWSMessageDecryptResult new];
result.envelopeData = envelopeData;
result.plaintextData = plaintextData;
result.source = source;
result.sourceDevice = sourceDevice;
result.isUDMessage = isUDMessage;
return result;
}
@end
#pragma mark -
@interface OWSMessageDecrypter ()
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
@property (nonatomic, readonly) SNSessionRestorationImplementation *sessionResetImplementation;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end
#pragma mark -
@implementation OWSMessageDecrypter
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
{
self = [super init];
if (!self) {
return self;
}
_primaryStorage = primaryStorage;
_sessionResetImplementation = [SNSessionRestorationImplementation new];
_dbConnection = primaryStorage.newDatabaseConnection;
OWSSingletonAssert();
return self;
}
#pragma mark - Dependencies
- (OWSBlockingManager *)blockingManager
{
OWSAssertDebug(SSKEnvironment.shared.blockingManager);
return SSKEnvironment.shared.blockingManager;
}
- (OWSIdentityManager *)identityManager
{
OWSAssertDebug(SSKEnvironment.shared.identityManager);
return SSKEnvironment.shared.identityManager;
}
- (id<OWSUDManager>)udManager
{
OWSAssertDebug(SSKEnvironment.shared.udManager);
return SSKEnvironment.shared.udManager;
}
- (TSAccountManager *)tsAccountManager
{
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
return SSKEnvironment.shared.tsAccountManager;
}
#pragma mark - Blocking
- (BOOL)isEnvelopeSenderBlocked:(SSKProtoEnvelope *)envelope
{
OWSAssertDebug(envelope);
return [self.blockingManager.blockedPhoneNumbers containsObject:envelope.source];
}
#pragma mark - Decryption
- (void)decryptEnvelope:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeData
successBlock:(DecryptSuccessBlock)successBlockParameter
failureBlock:(DecryptFailureBlock)failureBlockParameter
{
OWSAssertDebug(envelope);
OWSAssertDebug(envelopeData);
OWSAssertDebug(successBlockParameter);
OWSAssertDebug(failureBlockParameter);
OWSAssertDebug([self.tsAccountManager isRegistered]);
// successBlock is called synchronously so that we can avail ourselves of
// the transaction.
//
// Ensure that failureBlock is called on a worker queue.
DecryptFailureBlock failureBlock = ^() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
failureBlockParameter();
});
};
NSString *localRecipientId = self.tsAccountManager.localNumber;
uint32_t localDeviceId = OWSDevicePrimaryDeviceId;
DecryptSuccessBlock successBlock = ^(
OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
// Ensure all blocked messages are discarded.
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
return failureBlock();
}
if ([result.source isEqualToString:localRecipientId] && result.sourceDevice == localDeviceId) {
// Self-sent messages should be discarded during the decryption process.
OWSFailDebug(@"Unexpected self-sent sync message.");
return failureBlock();
}
// Having received a valid (decryptable) message from this user,
// make note of the fact that they have a valid Signal account.
[SignalRecipient markRecipientAsRegistered:result.source deviceId:result.sourceDevice transaction:transaction];
successBlockParameter(result, transaction);
};
@try {
OWSLogInfo(@"Decrypting envelope: %@.", [self descriptionForEnvelope:envelope]);
if (envelope.type != SSKProtoEnvelopeTypeUnidentifiedSender) {
if (!envelope.hasSource || envelope.source.length < 1 || ![ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) {
OWSFailDebug(@"Incoming envelope with invalid source.");
return failureBlock();
}
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
OWSFailDebug(@"Incoming envelope with invalid source device.");
return failureBlock();
}
// We block UD messages later, after they are decrypted.
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
return failureBlock();
}
}
switch (envelope.type) {
case SSKProtoEnvelopeTypeCiphertext: {
[self throws_decryptSecureMessage:envelope
envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"Decrypted secure message.");
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"Decrypting secure message from: %@ failed with error: %@.",
envelopeAddress(envelope),
error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
failureBlock();
}];
// Return to avoid double-acknowledging.
return;
}
case SSKProtoEnvelopeTypePrekeyBundle: {
[self throws_decryptPreKeyBundle:envelope
envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"Decrypted pre key bundle message.");
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"Decrypting pre key bundle message from: %@ failed with error: %@.",
envelopeAddress(envelope),
error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]);
failureBlock();
}];
// Return to avoid double-acknowledging.
return;
}
// These message types don't have a payload to decrypt.
case SSKProtoEnvelopeTypeReceipt:
case SSKProtoEnvelopeTypeKeyExchange:
case SSKProtoEnvelopeTypeUnknown: {
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
OWSMessageDecryptResult *result =
[OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:nil
source:envelope.source
sourceDevice:envelope.sourceDevice
isUDMessage:NO];
successBlock(result, transaction);
}];
// Return to avoid double-acknowledging.
return;
}
case SSKProtoEnvelopeTypeClosedGroupCiphertext: {
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSError *error = nil;
NSArray *plaintextAndSenderPublicKey = [LKClosedGroupUtilities decryptEnvelope:envelope transaction:transaction error:&error];
if (error != nil) { return failureBlock(); }
NSData *plaintext = plaintextAndSenderPublicKey[0];
NSString *senderPublicKey = plaintextAndSenderPublicKey[1];
SSKProtoEnvelopeBuilder *newEnvelope = [envelope asBuilder];
[newEnvelope setSource:senderPublicKey];
NSData *newEnvelopeAsData = [newEnvelope buildSerializedDataAndReturnError:&error];
if (error != nil) { return failureBlock(); }
NSString *userPublicKey = [OWSIdentityManager.sharedManager.identityKeyPair hexEncodedPublicKey];
if ([senderPublicKey isEqual:userPublicKey]) { return failureBlock(); }
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:newEnvelopeAsData
plaintextData:[plaintext removePadding]
source:senderPublicKey
sourceDevice:OWSDevicePrimaryDeviceId
isUDMessage:NO];
successBlock(result, transaction);
}];
return;
}
case SSKProtoEnvelopeTypeUnidentifiedSender: {
[self decryptUnidentifiedSender:envelope
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"Decrypted unidentified sender message.");
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"Decrypting unidentified sender message from: %@ failed with error: %@.",
envelopeAddress(envelope),
error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleUnidentifiedSenderMessage]);
failureBlock();
}];
// Return to avoid double-acknowledging.
return;
}
default:
OWSLogWarn(@"Received unhandled envelope type: %d.", (int)envelope.type);
break;
}
} @catch (NSException *exception) {
OWSFailDebug(@"Received an invalid envelope: %@.", exception.debugDescription);
OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]);
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
[SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage
transaction:transaction];
}];
}
failureBlock();
}
- (void)throws_decryptSecureMessage:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeData
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
OWSAssertDebug(envelope);
OWSAssertDebug(envelopeData);
OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock);
[self decryptEnvelope:envelope
envelopeData:envelopeData
cipherTypeName:@"Secure Message"
cipherMessageBlock:^(NSData *encryptedData) {
return [[WhisperMessage alloc] init_throws_withData:encryptedData];
}
successBlock:successBlock
failureBlock:failureBlock];
}
- (void)throws_decryptPreKeyBundle:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeData
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
OWSAssertDebug(envelope);
OWSAssertDebug(envelopeData);
OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock);
// Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage.
[TSPreKeyManager checkPreKeys];
[self decryptEnvelope:envelope
envelopeData:envelopeData
cipherTypeName:@"PreKey Bundle"
cipherMessageBlock:^(NSData *encryptedData) {
return [[PreKeyWhisperMessage alloc] init_throws_withData:encryptedData];
}
successBlock:successBlock
failureBlock:failureBlock];
}
- (void)decryptEnvelope:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeData
cipherTypeName:(NSString *)cipherTypeName
cipherMessageBlock:(id<CipherMessage> (^_Nonnull)(NSData *))cipherMessageBlock
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
OWSAssertDebug(envelope);
OWSAssertDebug(envelopeData);
OWSAssertDebug(cipherTypeName.length > 0);
OWSAssertDebug(cipherMessageBlock);
OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock);
NSString *recipientId = envelope.source;
int deviceId = envelope.sourceDevice;
// DEPRECATED - Remove `legacyMessage` after all clients have been upgraded.
NSData *encryptedData = envelope.content ?: envelope.legacyMessage;
if (!encryptedData) {
OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, @"Envelope has no content.");
return failureBlock(error);
}
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
id<CipherMessage> cipherMessage = cipherMessageBlock(encryptedData);
SNSessionCipher *cipher = [[SNSessionCipher alloc]
initWithSessionResetImplementation:self.sessionResetImplementation
sessionStore:self.primaryStorage
preKeyStore:self.primaryStorage
signedPreKeyStore:self.primaryStorage
identityKeyStore:self.identityManager
recipientID:recipientId
deviceID:deviceId];
// plaintextData may be nil for some envelope types.
NSError *error = nil;
NSData *_Nullable decryptedData = [cipher decrypt:cipherMessage protocolContext:transaction error:&error];
// Throw if we got an error
SCKRaiseIfExceptionWrapperError(error);
NSData *_Nullable plaintextData = decryptedData != nil ? [decryptedData removePadding] : nil;
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:plaintextData
source:envelope.source
sourceDevice:envelope.sourceDevice
isUDMessage:NO];
successBlock(result, transaction);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:exception envelope:envelope];
NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting %@: %@.", cipherTypeName, exception.description];
OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error);
});
}
}];
}
- (void)decryptUnidentifiedSender:(SSKProtoEnvelope *)envelope
successBlock:(DecryptSuccessBlock)successBlock
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
{
OWSAssertDebug(envelope);
OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock);
// NOTE: We don't need to bother with `legacyMessage` for UD messages.
NSData *encryptedData = envelope.content;
if (!encryptedData) {
NSString *errorDescription = @"UD Envelope is missing content.";
OWSFailDebug(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, errorDescription);
return failureBlock(error);
}
UInt64 serverTimestamp = envelope.timestamp;
id<SMKCertificateValidator> certificateValidator =
[[SMKCertificateDefaultValidator alloc] initWithTrustRoot:self.udManager.trustRoot];
NSString *localRecipientId = self.tsAccountManager.localNumber;
uint32_t localDeviceId = OWSDevicePrimaryDeviceId;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSError *cipherError;
SMKSecretSessionCipher *_Nullable cipher =
[[SMKSecretSessionCipher alloc] initWithSessionResetImplementation:self.sessionResetImplementation
sessionStore:self.primaryStorage
preKeyStore:self.primaryStorage
signedPreKeyStore:self.primaryStorage
identityStore:self.identityManager
error:&cipherError];
if (cipherError || !cipher) {
OWSFailDebug(@"Could not create secret session cipher: %@.", cipherError);
cipherError = EnsureDecryptError(cipherError, @"Could not create secret session cipher.");
return failureBlock(cipherError);
}
NSError *decryptError;
SMKDecryptResult *_Nullable decryptResult =
[cipher throwswrapped_decryptMessageWithCertificateValidator:certificateValidator
cipherTextData:encryptedData
timestamp:serverTimestamp
localRecipientId:localRecipientId
localDeviceId:localDeviceId
protocolContext:transaction
error:&decryptError];
if (!decryptResult) {
if (!decryptError) {
OWSFailDebug(@"Caller should provide specific error.");
NSError *error = OWSErrorWithCodeDescription(
OWSErrorCodeFailedToDecryptUDMessage, @"Could not decrypt UD message.");
return failureBlock(error);
}
// Decrypt Failure Part 1: Unwrap failure details
NSError *_Nullable underlyingError;
SSKProtoEnvelope *_Nullable identifiedEnvelope;
if (![decryptError.domain isEqualToString:@"SessionMetadataKit.SecretSessionKnownSenderError"]) {
underlyingError = decryptError;
identifiedEnvelope = envelope;
} else {
underlyingError = decryptError.userInfo[NSUnderlyingErrorKey];
NSString *senderRecipientId
= decryptError.userInfo[SecretSessionKnownSenderError.kSenderRecipientIdKey];
OWSAssert(senderRecipientId);
NSNumber *senderDeviceId = decryptError.userInfo[SecretSessionKnownSenderError.kSenderDeviceIdKey];
OWSAssert(senderDeviceId);
SSKProtoEnvelopeBuilder *identifiedEnvelopeBuilder = envelope.asBuilder;
identifiedEnvelopeBuilder.source = senderRecipientId;
identifiedEnvelopeBuilder.sourceDevice = senderDeviceId.unsignedIntValue;
NSError *identifiedEnvelopeBuilderError;
identifiedEnvelope = [identifiedEnvelopeBuilder buildAndReturnError:&identifiedEnvelopeBuilderError];
if (identifiedEnvelopeBuilderError) {
OWSFailDebug(@"identifiedEnvelopeBuilderError: %@", identifiedEnvelopeBuilderError);
}
}
OWSAssert(underlyingError);
OWSAssert(identifiedEnvelope);
NSException *_Nullable underlyingException;
if ([underlyingError.domain isEqualToString:SCKExceptionWrapperErrorDomain]
&& underlyingError.code == SCKExceptionWrapperErrorThrown) {
underlyingException = underlyingError.userInfo[SCKExceptionWrapperUnderlyingExceptionKey];
OWSAssert(underlyingException);
}
// Decrypt Failure Part 2: Handle unwrapped failure details
if (underlyingException) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:underlyingException envelope:identifiedEnvelope];
NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting UD message: %@.", underlyingException.description];
OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error);
});
return;
}
if ([underlyingError.domain isEqualToString:@"SessionMetadataKit.SMKSecretSessionCipherError"]
&& underlyingError.code == SMKSecretSessionCipherErrorSelfSentMessage) {
// Self-sent messages can be safely discarded.
failureBlock(underlyingError);
return;
}
// Attempt to recover automatically
if ([decryptError userInfo][NSUnderlyingErrorKey] != nil) {
NSDictionary *underlyingErrorUserInfo = [[decryptError userInfo][NSUnderlyingErrorKey] userInfo];
if (underlyingErrorUserInfo[SCKExceptionWrapperUnderlyingExceptionKey] != nil) {
NSException *underlyingUnderlyingError = underlyingErrorUserInfo[SCKExceptionWrapperUnderlyingExceptionKey];
if ([[underlyingUnderlyingError reason] hasPrefix:@"Bad Mac!"]) {
if ([underlyingError userInfo][@"kSenderRecipientIdKey"] != nil) {
NSString *senderPublicKey = [underlyingError userInfo][@"kSenderRecipientIdKey"];
TSContactThread *thread = [TSContactThread getThreadWithContactId:senderPublicKey transaction:transaction];
if (thread != nil) {
[thread addSessionRestoreDevice:senderPublicKey transaction:transaction];
[LKSessionManagementProtocol startSessionResetInThread:thread transaction:transaction];
}
}
}
}
}
failureBlock(underlyingError);
return;
}
if (decryptResult.messageType == SMKMessageTypePrekey) {
[TSPreKeyManager checkPreKeys];
}
NSString *source = decryptResult.senderRecipientId;
if (source.length < 1) {
NSString *errorDescription = @"Invalid UD sender.";
OWSFailDebug(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, errorDescription);
return failureBlock(error);
}
long sourceDeviceId = decryptResult.senderDeviceId;
if (sourceDeviceId < 1 || sourceDeviceId > UINT32_MAX) {
NSString *errorDescription = @"Invalid UD sender device ID.";
OWSFailDebug(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, errorDescription);
return failureBlock(error);
}
NSData *plaintextData = [decryptResult.paddedPayload removePadding];
SSKProtoEnvelopeBuilder *envelopeBuilder = [envelope asBuilder];
[envelopeBuilder setSource:source];
[envelopeBuilder setSourceDevice:(uint32_t)sourceDeviceId];
if (decryptResult.messageType == SMKMessageTypeFallback) {
[envelopeBuilder setType:SSKProtoEnvelopeTypeFallbackMessage];
OWSLogInfo(@"SMKMessageTypeFallback");
}
NSError *envelopeBuilderError;
NSData *_Nullable newEnvelopeData = [envelopeBuilder buildSerializedDataAndReturnError:&envelopeBuilderError];
if (envelopeBuilderError || !newEnvelopeData) {
OWSFailDebug(@"Could not update UD envelope data: %@", envelopeBuilderError);
NSError *error = EnsureDecryptError(envelopeBuilderError, @"Could not update UD envelope data");
return failureBlock(error);
}
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:newEnvelopeData
plaintextData:plaintextData
source:source
sourceDevice:(uint32_t)sourceDeviceId
isUDMessage:YES];
successBlock(result, transaction);
}];
}
- (void)processException:(NSException *)exception envelope:(SSKProtoEnvelope *)envelope
{
OWSLogError(
@"Got exception: %@ of type: %@ with reason: %@", exception.description, exception.name, exception.reason);
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage;
if (envelope.source.length == 0) {
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
[SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage
transaction:transaction];
return;
}
if ([exception.name isEqualToString:NoSessionException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope);
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKey], envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyIdException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKeyId], envelope);
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:DuplicateMessageException]) {
// Duplicate messages are silently discarded.
return;
} else if ([exception.name isEqualToString:InvalidVersionException]) {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion], envelope);
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// Should no longer get here, since we now record the new identity for incoming messages.
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException], envelope);
OWSFailDebug(@"Failed to trust identity on incoming message from: %@", envelopeAddress(envelope));
return;
} else {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope);
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
}
OWSAssertDebug(errorMessage);
if (errorMessage != nil) {
[LKSessionManagementProtocol handleDecryptionError:errorMessage forPublicKey:envelope.source transaction:transaction];
if (![LKSessionMetaProtocol isErrorMessageFromBeforeRestoration:errorMessage]) {
[errorMessage saveWithTransaction:transaction];
[self notifyUserForErrorMessage:errorMessage envelope:envelope transaction:transaction];
} else {
// Show the thread if it exists before restoration
NSString *masterPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source;
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterPublicKey transaction:transaction];
contactThread.shouldThreadBeVisible = true;
[contactThread saveWithTransaction:transaction];
}
}
}];
}
- (void)notifyUserForErrorMessage:(TSErrorMessage *)errorMessage
envelope:(SSKProtoEnvelope *)envelope
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
NSString *masterPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source;
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterPublicKey transaction:transaction];
[SSKEnvironment.shared.notificationsManager notifyUserForErrorMessage:errorMessage
thread:contactThread
transaction:transaction];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,24 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class SSKProtoContent;
@class SSKProtoDataMessage;
@class SSKProtoEnvelope;
NSString *envelopeAddress(SSKProtoEnvelope *envelope);
@interface OWSMessageHandler : NSObject
- (NSString *)descriptionForEnvelopeType:(SSKProtoEnvelope *)envelope;
- (NSString *)descriptionForEnvelope:(SSKProtoEnvelope *)envelope;
- (NSString *)descriptionForContent:(SSKProtoContent *)content;
- (NSString *)descriptionForDataMessage:(SSKProtoDataMessage *)dataMessage;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,183 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSMessageHandler.h"
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
// used in log formatting
NSString *envelopeAddress(SSKProtoEnvelope *envelope)
{
return [NSString stringWithFormat:@"%@.%d", envelope.source, (unsigned int)envelope.sourceDevice];
}
@implementation OWSMessageHandler
- (NSString *)descriptionForEnvelopeType:(SSKProtoEnvelope *)envelope
{
OWSAssertDebug(envelope != nil);
switch (envelope.type) {
case SSKProtoEnvelopeTypeReceipt:
return @"DeliveryReceipt";
case SSKProtoEnvelopeTypeUnknown:
// Shouldn't happen
return @"Unknown";
case SSKProtoEnvelopeTypeCiphertext:
return @"SignalEncryptedMessage";
case SSKProtoEnvelopeTypeKeyExchange:
// Unsupported
return @"KeyExchange";
case SSKProtoEnvelopeTypePrekeyBundle:
return @"PreKeyEncryptedMessage";
case SSKProtoEnvelopeTypeUnidentifiedSender:
return @"UnidentifiedSender";
case SSKProtoEnvelopeTypeFallbackMessage:
return @"FallbackMessage";
case SSKProtoEnvelopeTypeClosedGroupCiphertext:
return @"ClosedGroupCiphertext";
default:
// Shouldn't happen
return @"Other";
}
}
- (NSString *)descriptionForEnvelope:(SSKProtoEnvelope *)envelope
{
OWSAssertDebug(envelope != nil);
return [NSString stringWithFormat:@"<Envelope type: %@, source: %@, timestamp: %llu content.length: %lu />",
[self descriptionForEnvelopeType:envelope],
envelopeAddress(envelope),
envelope.timestamp,
(unsigned long)envelope.content.length];
}
/**
* We don't want to just log `content.description` because we'd potentially log message bodies for dataMesssages and
* sync transcripts
*/
- (NSString *)descriptionForContent:(SSKProtoContent *)content
{
if (content.syncMessage) {
return [NSString stringWithFormat:@"<SyncMessage: %@ />", [self descriptionForSyncMessage:content.syncMessage]];
} else if (content.dataMessage) {
return [NSString stringWithFormat:@"<DataMessage: %@ />", [self descriptionForDataMessage:content.dataMessage]];
} else if (content.callMessage) {
NSString *callMessageDescription = [self descriptionForCallMessage:content.callMessage];
return [NSString stringWithFormat:@"<CallMessage %@ />", callMessageDescription];
} else if (content.nullMessage) {
return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage];
} else if (content.receiptMessage) {
return [NSString stringWithFormat:@"<ReceiptMessage: %@ />", content.receiptMessage];
} else if (content.typingMessage) {
return [NSString stringWithFormat:@"<TypingMessage: %@ />", content.typingMessage];
} else {
// Don't fire an analytics event; if we ever add a new content type, we'd generate a ton of
// analytics traffic.
return @"UnknownContent";
}
}
- (NSString *)descriptionForCallMessage:(SSKProtoCallMessage *)callMessage
{
NSString *messageType;
UInt64 callId;
if (callMessage.offer) {
messageType = @"Offer";
callId = callMessage.offer.id;
} else if (callMessage.busy) {
messageType = @"Busy";
callId = callMessage.busy.id;
} else if (callMessage.answer) {
messageType = @"Answer";
callId = callMessage.answer.id;
} else if (callMessage.hangup) {
messageType = @"Hangup";
callId = callMessage.hangup.id;
} else if (callMessage.iceUpdate.count > 0) {
messageType = [NSString stringWithFormat:@"Ice Updates (%lu)", (unsigned long)callMessage.iceUpdate.count];
callId = callMessage.iceUpdate.firstObject.id;
} else {
OWSFailDebug(@"failure: unexpected call message type: %@", callMessage);
messageType = @"Unknown";
callId = 0;
}
return [NSString stringWithFormat:@"type: %@, id: %llu", messageType, callId];
}
/**
* We don't want to just log `dataMessage.description` because we'd potentially log message contents
*/
- (NSString *)descriptionForDataMessage:(SSKProtoDataMessage *)dataMessage
{
NSMutableString *description = [NSMutableString new];
if (dataMessage.group) {
[description appendString:@"(Group:YES) "];
}
if ((dataMessage.flags & SSKProtoDataMessageFlagsEndSession) != 0) {
[description appendString:@"EndSession"];
} else if ((dataMessage.flags & SSKProtoDataMessageFlagsExpirationTimerUpdate) != 0) {
[description appendString:@"ExpirationTimerUpdate"];
} else if ((dataMessage.flags & SSKProtoDataMessageFlagsProfileKeyUpdate) != 0) {
[description appendString:@"ProfileKey"];
} else if (dataMessage.attachments.count > 0) {
[description appendString:@"MessageWithAttachment"];
} else {
[description appendString:@"Plain"];
}
return [NSString stringWithFormat:@"<%@ />", description];
}
/**
* We don't want to just log `syncMessage.description` because we'd potentially log message contents in sent transcripts
*/
- (NSString *)descriptionForSyncMessage:(SSKProtoSyncMessage *)syncMessage
{
NSMutableString *description = [NSMutableString new];
if (syncMessage.sent) {
[description appendString:@"SentTranscript"];
} else if (syncMessage.request) {
if (syncMessage.request.type == SSKProtoSyncMessageRequestTypeContacts) {
[description appendString:@"ContactRequest"];
} else if (syncMessage.request.type == SSKProtoSyncMessageRequestTypeGroups) {
[description appendString:@"GroupRequest"];
} else if (syncMessage.request.type == SSKProtoSyncMessageRequestTypeBlocked) {
[description appendString:@"BlockedRequest"];
} else if (syncMessage.request.type == SSKProtoSyncMessageRequestTypeConfiguration) {
[description appendString:@"ConfigurationRequest"];
} else {
OWSFailDebug(@"Unknown sync message request type");
[description appendString:@"UnknownRequest"];
}
} else if (syncMessage.blocked) {
[description appendString:@"Blocked"];
} else if (syncMessage.read.count > 0) {
[description appendString:@"ReadReceipt"];
} else if (syncMessage.verified) {
NSString *verifiedString =
[NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination];
[description appendString:verifiedString];
} else if (syncMessage.contacts) {
[description appendString:@"Contacts"];
} else if (syncMessage.groups) {
[description appendString:@"ClosedGroups"];
} else if (syncMessage.openGroups) {
[description appendString:@"OpenGroups"];
} else {
[description appendString:@"Unknown"];
}
return description;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,33 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/OWSMessageHandler.h>
NS_ASSUME_NONNULL_BEGIN
@class OWSPrimaryStorage;
@class SSKProtoEnvelope;
@class TSThread;
@class YapDatabaseReadWriteTransaction;
@interface OWSMessageManager : OWSMessageHandler
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedManager;
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
// processEnvelope: can be called from any thread.
- (void)throws_processEnvelope:(SSKProtoEnvelope *)envelope
plaintextData:(NSData *_Nullable)plaintextData
wasReceivedByUD:(BOOL)wasReceivedByUD
transaction:(YapDatabaseReadWriteTransaction *)transaction
serverID:(uint64_t)serverID;
// This should be invoked by the main app when the app is ready.
- (void)startObserving;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class OWSPrimaryStorage;
@class OWSStorage;
// This class is used to write incoming (encrypted, unprocessed)
// messages to a durable queue and then decrypt them in the order
// in which they were received. Successfully decrypted messages
// are forwarded to OWSBatchMessageProcessor.
@interface OWSMessageReceiver : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
+ (NSString *)databaseExtensionName;
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage;
- (void)handleReceivedEnvelopeData:(NSData *)envelopeData;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,513 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSMessageReceiver.h"
#import "AppContext.h"
#import "AppReadiness.h"
#import "NSArray+OWS.h"
#import "NotificationsProtocol.h"
#import "OWSBackgroundTask.h"
#import "OWSBatchMessageProcessor.h"
#import "OWSMessageDecrypter.h"
#import "OWSPrimaryStorage+Loki.h"
#import "OWSQueues.h"
#import "OWSStorage.h"
#import "OWSIdentityManager.h"
#import "SSKEnvironment.h"
#import "TSAccountManager.h"
#import "TSDatabaseView.h"
#import "TSErrorMessage.h"
#import "TSYapDatabaseObject.h"
#import <SignalCoreKit/Threading.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <YapDatabase/YapDatabaseAutoView.h>
#import <YapDatabase/YapDatabaseConnection.h>
#import <YapDatabase/YapDatabaseTransaction.h>
#import <YapDatabase/YapDatabaseViewTypes.h>
#import <SignalCoreKit/NSDate+OWS.h>
#import "SSKAsserts.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSMessageDecryptJob : TSYapDatabaseObject
@property (nonatomic, readonly) NSDate *createdAt;
@property (nonatomic, readonly) NSData *envelopeData;
@property (nonatomic, readonly, nullable) SSKProtoEnvelope *envelopeProto;
- (instancetype)initWithEnvelopeData:(NSData *)envelopeData NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_UNAVAILABLE;
@end
#pragma mark -
@implementation OWSMessageDecryptJob
+ (NSString *)collection
{
return @"OWSMessageProcessingJob";
}
- (instancetype)initWithEnvelopeData:(NSData *)envelopeData
{
OWSAssertDebug(envelopeData);
self = [super initWithUniqueId:[NSUUID new].UUIDString];
if (!self) {
return self;
}
_envelopeData = envelopeData;
_createdAt = [NSDate new];
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
return [super initWithCoder:coder];
}
- (nullable SSKProtoEnvelope *)envelopeProto
{
NSError *error;
SSKProtoEnvelope *_Nullable envelope = [SSKProtoEnvelope parseData:self.envelopeData error:&error];
if (error || envelope == nil) {
OWSFailDebug(@"failed to parse envelope with error: %@", error);
return nil;
}
return envelope;
}
@end
#pragma mark - Finder
NSString *const OWSMessageDecryptJobFinderExtensionName = @"OWSMessageProcessingJobFinderExtensionName2";
NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessingJobFinderExtensionGroup2";
@interface OWSMessageDecryptJobFinder : NSObject
@end
#pragma mark -
@interface OWSMessageDecryptJobFinder ()
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end
#pragma mark -
@implementation OWSMessageDecryptJobFinder
- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection
{
OWSSingletonAssert();
self = [super init];
if (!self) {
return self;
}
_dbConnection = dbConnection;
[OWSMessageDecryptJobFinder registerLegacyClasses];
return self;
}
- (OWSMessageDecryptJob *_Nullable)nextJob
{
__block OWSMessageDecryptJob *_Nullable job = nil;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSMessageDecryptJobFinderExtensionName];
OWSAssertDebug(viewTransaction != nil);
job = [viewTransaction firstObjectInGroup:OWSMessageDecryptJobFinderExtensionGroup];
}];
return job;
}
- (void)addJobForEnvelopeData:(NSData *)envelopeData
{
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
OWSMessageDecryptJob *job = [[OWSMessageDecryptJob alloc] initWithEnvelopeData:envelopeData];
[job saveWithTransaction:transaction];
}];
}
- (void)removeJobWithId:(NSString *)uniqueId
{
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction removeObjectForKey:uniqueId inCollection:[OWSMessageDecryptJob collection]];
}];
}
+ (YapDatabaseView *)databaseExtension
{
YapDatabaseViewSorting *sorting =
[YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction,
NSString *group,
NSString *collection1,
NSString *key1,
id object1,
NSString *collection2,
NSString *key2,
id object2) {
if (![object1 isKindOfClass:[OWSMessageDecryptJob class]]) {
OWSFailDebug(@"Unexpected object: %@ in collection: %@", [object1 class], collection1);
return NSOrderedSame;
}
OWSMessageDecryptJob *job1 = (OWSMessageDecryptJob *)object1;
if (![object2 isKindOfClass:[OWSMessageDecryptJob class]]) {
OWSFailDebug(@"Unexpected object: %@ in collection: %@", [object2 class], collection2);
return NSOrderedSame;
}
OWSMessageDecryptJob *job2 = (OWSMessageDecryptJob *)object2;
return [job1.createdAt compare:job2.createdAt];
}];
YapDatabaseViewGrouping *grouping =
[YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable(YapDatabaseReadTransaction *_Nonnull transaction,
NSString *_Nonnull collection,
NSString *_Nonnull key,
id _Nonnull object) {
if (![object isKindOfClass:[OWSMessageDecryptJob class]]) {
OWSFailDebug(@"Unexpected object: %@ in collection: %@", object, collection);
return nil;
}
// Arbitrary string - all in the same group. We're only using the view for sorting.
return OWSMessageDecryptJobFinderExtensionGroup;
}];
YapDatabaseViewOptions *options = [YapDatabaseViewOptions new];
options.allowedCollections =
[[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[OWSMessageDecryptJob collection]]];
return [[YapDatabaseAutoView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"1" options:options];
}
+ (void)registerLegacyClasses
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// We've renamed OWSMessageProcessingJob to OWSMessageDecryptJob.
[NSKeyedUnarchiver setClass:[OWSMessageDecryptJob class] forClassName:[OWSMessageDecryptJob collection]];
});
}
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
[self registerLegacyClasses];
YapDatabaseView *existingView = [storage registeredExtension:OWSMessageDecryptJobFinderExtensionName];
if (existingView) {
OWSFailDebug(@"%@ was already initialized.", OWSMessageDecryptJobFinderExtensionName);
// already initialized
return;
}
[storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName];
}
@end
#pragma mark - Queue Processing
@interface OWSMessageDecryptQueue : NSObject
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) OWSMessageDecryptJobFinder *finder;
@property (nonatomic) BOOL isDrainingQueue;
- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection
finder:(OWSMessageDecryptJobFinder *)finder NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
#pragma mark -
@implementation OWSMessageDecryptQueue
- (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection finder:(OWSMessageDecryptJobFinder *)finder
{
OWSSingletonAssert();
self = [super init];
if (!self) {
return self;
}
_dbConnection = dbConnection;
_finder = finder;
_isDrainingQueue = NO;
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (CurrentAppContext().isMainApp) {
[self drainQueue];
}
}];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(registrationStateDidChange:)
name:RegistrationStateDidChangeNotification
object:nil];
return self;
}
#pragma mark - Singletons
- (OWSMessageDecrypter *)messageDecrypter
{
OWSAssertDebug(SSKEnvironment.shared.messageDecrypter);
return SSKEnvironment.shared.messageDecrypter;
}
- (OWSBatchMessageProcessor *)batchMessageProcessor
{
OWSAssertDebug(SSKEnvironment.shared.batchMessageProcessor);
return SSKEnvironment.shared.batchMessageProcessor;
}
- (TSAccountManager *)tsAccountManager
{
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
return SSKEnvironment.shared.tsAccountManager;
}
#pragma mark - Notifications
- (void)registrationStateDidChange:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (CurrentAppContext().isMainApp) {
[self drainQueue];
}
}];
}
#pragma mark - Instance methods
- (dispatch_queue_t)serialQueue
{
static dispatch_queue_t queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.whispersystems.message.decrypt", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
- (void)enqueueEnvelopeData:(NSData *)envelopeData
{
[self.finder addJobForEnvelopeData:envelopeData];
}
- (void)drainQueue
{
OWSAssertDebug(AppReadiness.isAppReady);
if (!CurrentAppContext().isMainApp) { return; }
if (!self.tsAccountManager.isRegisteredAndReady) { return; }
dispatch_async(self.serialQueue, ^{
if (self.isDrainingQueue) { return; }
self.isDrainingQueue = YES;
[self drainQueueWorkStep];
});
}
- (void)drainQueueWorkStep
{
AssertOnDispatchQueue(self.serialQueue);
OWSMessageDecryptJob *_Nullable job = [self.finder nextJob];
if (!job) {
self.isDrainingQueue = NO;
OWSLogVerbose(@"Queue is drained.");
return;
}
__block OWSBackgroundTask *_Nullable backgroundTask =
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
[self processJob:job
completion:^(BOOL success) {
[self.finder removeJobWithId:job.uniqueId];
OWSLogVerbose(@"%@ job. %lu jobs left.",
success ? @"decrypted" : @"failed to decrypt",
(unsigned long)[OWSMessageDecryptJob numberOfKeysInCollection]);
[self drainQueueWorkStep];
OWSAssertDebug(backgroundTask);
backgroundTask = nil;
}];
}
- (BOOL)wasReceivedByUD:(SSKProtoEnvelope *)envelope
{
return (envelope.type == SSKProtoEnvelopeTypeUnidentifiedSender && (!envelope.hasSource || envelope.source.length < 1));
}
- (void)processJob:(OWSMessageDecryptJob *)job completion:(void (^)(BOOL))completion
{
AssertOnDispatchQueue(self.serialQueue);
OWSAssertDebug(job);
SSKProtoEnvelope *_Nullable envelope = job.envelopeProto;
if (!envelope) {
OWSFailDebug(@"Couldn't parse proto.");
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
[SSKEnvironment.shared.notificationsManager notifyUserForThreadlessErrorMessage:errorMessage
transaction:transaction];
}];
dispatch_async(self.serialQueue, ^{
completion(NO);
});
return;
}
// We use the original envelope for this check;
// the decryption process might rewrite the envelope.
BOOL wasReceivedByUD = [self wasReceivedByUD:envelope];
[self.messageDecrypter decryptEnvelope:envelope
envelopeData:job.envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSAssertDebug(transaction);
if ([LKSessionMetaProtocol shouldSkipMessageDecryptResult:result wrappedIn:envelope]) {
dispatch_async(self.serialQueue, ^{
completion(YES);
});
return;
}
// We persist the decrypted envelope data in the same transaction within which
// it was decrypted to prevent data loss. If the new job isn't persisted,
// the session state side effects of its decryption are also rolled back.
//
// NOTE: We use envelopeData from the decrypt result, not job.envelopeData,
// since the envelope may be altered by the decryption process in the UD case.
[self.batchMessageProcessor enqueueEnvelopeData:result.envelopeData
plaintextData:result.plaintextData
wasReceivedByUD:wasReceivedByUD
transaction:transaction];
dispatch_async(self.serialQueue, ^{
completion(YES);
});
}
failureBlock:^{
dispatch_async(self.serialQueue, ^{
completion(NO);
});
}];
}
@end
#pragma mark - OWSMessageReceiver
@interface OWSMessageReceiver ()
@property (nonatomic, readonly) OWSMessageDecryptQueue *processingQueue;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end
#pragma mark -
@implementation OWSMessageReceiver
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
{
OWSSingletonAssert();
self = [super init];
if (!self) {
return self;
}
// For coherency we use the same dbConnection to persist and read the unprocessed envelopes
YapDatabaseConnection *dbConnection = [primaryStorage newDatabaseConnection];
OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection];
OWSMessageDecryptQueue *processingQueue = [[OWSMessageDecryptQueue alloc] initWithDBConnection:dbConnection finder:finder];
_processingQueue = processingQueue;
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (CurrentAppContext().isMainApp) {
[self.processingQueue drainQueue];
}
}];
return self;
}
#pragma mark - class methods
+ (NSString *)databaseExtensionName
{
return OWSMessageDecryptJobFinderExtensionName;
}
+ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage
{
[OWSMessageDecryptJobFinder asyncRegisterDatabaseExtension:storage];
}
#pragma mark - instance methods
- (void)handleReceivedEnvelopeData:(NSData *)envelopeData
{
if (envelopeData.length < 1) {
OWSFailDebug(@"Received an empty envelope.");
return;
}
// Drop any too-large messages on the floor. Well behaving clients should never send them.
NSUInteger kMaxEnvelopeByteCount = 250 * 1024;
if (envelopeData.length > kMaxEnvelopeByteCount) {
OWSFailDebug(@"Received an oversized message.");
return;
}
// Take note of any messages larger than we expect, but still process them.
// This likely indicates a misbehaving sending client.
NSUInteger kLargeEnvelopeWarningByteCount = 25 * 1024;
if (envelopeData.length > kLargeEnvelopeWarningByteCount) {
OWSFailDebug(@"Received an unexpectedly large message.");
}
[self.processingQueue enqueueEnvelopeData:envelopeData];
[self.processingQueue drainQueue];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,96 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
// Corresponds to a single effort to send a message to a given recipient,
// which may span multiple attempts. Note that group messages may be sent
// to multiple recipients and therefore require multiple instances of
// OWSMessageSend.
@objc
public class OWSMessageSend: NSObject {
@objc
public let message: TSOutgoingMessage
// thread may be nil if message is an OWSOutgoingSyncMessage.
@objc
public let thread: TSThread?
@objc
public let recipient: SignalRecipient
private static let kMaxRetriesPerRecipient: Int = 1 // Loki: We have our own retrying
@objc
public var remainingAttempts = OWSMessageSend.kMaxRetriesPerRecipient
// We "fail over" to REST sends after _any_ error sending
// via the web socket.
@objc
public var hasWebsocketSendFailed = false
@objc
public var udAccess: OWSUDAccess?
@objc
public var senderCertificate: SMKSenderCertificate?
@objc
public let localNumber: String
@objc
public let isLocalNumber: Bool
@objc
public let success: () -> Void
@objc
public let failure: (Error) -> Void
@objc
public init(message: TSOutgoingMessage,
thread: TSThread?,
recipient: SignalRecipient,
senderCertificate: SMKSenderCertificate?,
udAccess: OWSUDAccess?,
localNumber: String,
success: @escaping () -> Void,
failure: @escaping (Error) -> Void) {
self.message = message
self.thread = thread
self.recipient = recipient
self.localNumber = localNumber
self.senderCertificate = senderCertificate
self.udAccess = udAccess
if let recipientId = recipient.uniqueId {
self.isLocalNumber = localNumber == recipientId
} else {
owsFailDebug("SignalRecipient missing recipientId")
self.isLocalNumber = false
}
self.success = success
self.failure = failure
}
@objc
public var isUDSend: Bool {
return udAccess != nil && senderCertificate != nil
}
@objc
public func disableUD() {
Logger.verbose("\(recipient.recipientId)")
udAccess = nil
}
@objc
public func setHasUDAuthFailed() {
Logger.verbose("\(recipient.recipientId)")
// We "fail over" to non-UD sends after auth errors sending via UD.
disableUD()
}
}

View File

@ -1,121 +0,0 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <SignalUtilitiesKit/DataSource.h>
#import <SignalUtilitiesKit/TSContactThread.h>
NS_ASSUME_NONNULL_BEGIN
extern const NSUInteger kOversizeTextMessageSizeThreshold;
@class OWSBlockingManager;
@class OWSPrimaryStorage;
@class TSAttachmentStream;
@class TSInvalidIdentityKeySendingErrorMessage;
@class TSNetworkManager;
@class TSOutgoingMessage;
@class TSThread;
@class YapDatabaseReadWriteTransaction;
@class OWSMessageSend;
@protocol ContactsManagerProtocol;
/**
* Useful for when you *sometimes* want to retry before giving up and calling the failure handler
* but *sometimes* we don't want to retry when we know it's a terminal failure, so we allow the
* caller to indicate this with isRetryable=NO.
*/
typedef void (^RetryableFailureHandler)(NSError *_Nonnull error);
// Message send error handling is slightly different for contact and group messages.
//
// For example, If one member of a group deletes their account, the group should
// ignore errors when trying to send messages to this ex-member.
#pragma mark -
NS_SWIFT_NAME(OutgoingAttachmentInfo)
@interface OWSOutgoingAttachmentInfo : NSObject
@property (nonatomic, readonly) DataSource *dataSource;
@property (nonatomic, readonly) NSString *contentType;
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
@property (nonatomic, readonly, nullable) NSString *caption;
@property (nonatomic, readonly, nullable) NSString *albumMessageId;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDataSource:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
caption:(nullable NSString *)caption
albumMessageId:(nullable NSString *)albumMessageId NS_DESIGNATED_INITIALIZER;
@end
#pragma mark -
NS_SWIFT_NAME(MessageSender)
@interface OWSMessageSender : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER;
/**
* Send and resend text messages or resend messages with existing attachments.
* If you haven't yet created the attachment, see the `sendAttachment:` variants.
*/
- (void)sendMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Takes care of allocating and uploading the attachment, then sends the message.
* Only necessary to call once. If sending fails, retry with `sendMessage:`.
*/
- (void)sendAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
albumMessageId:(nullable NSString *)albumMessageId
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)sendAttachments:(NSArray<OWSOutgoingAttachmentInfo *> *)attachmentInfos
inMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Same as `sendAttachment:`, but deletes the local copy of the attachment after sending.
* Used for sending sync request data, not for user visible attachments.
*/
- (void)sendTemporaryAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)sendMessage:(OWSMessageSend *)messageSend;
@end
#pragma mark -
@interface OutgoingMessagePreparer : NSObject
/// Persists all necessary data to disk before sending, e.g. generate thumbnails
+ (NSArray<NSString *> *)prepareMessageForSending:(TSOutgoingMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction;
/// Writes attachment to disk and applies original filename to message attributes
+ (void)prepareAttachments:(NSArray<OWSOutgoingAttachmentInfo *> *)attachmentInfos
inMessage:(TSOutgoingMessage *)outgoingMessage
completionHandler:(void (^)(NSError *_Nullable error))completionHandler
NS_SWIFT_NAME(prepareAttachments(_:inMessage:completionHandler:));
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ public final class Poller : NSObject {
// MARK: Private API
private func setUpPolling() {
guard isPolling else { return }
SnodeAPI.getSwarm(for: getUserHexEncodedPublicKey(), isForcedReload: true).then2 { [weak self] _ -> Promise<Void> in
let _ = SnodeAPI.getSwarm(for: getUserHexEncodedPublicKey(), isForcedReload: true).then2 { [weak self] _ -> Promise<Void> in
guard let strongSelf = self else { return Promise { $0.fulfill(()) } }
strongSelf.usedSnodes.removeAll()
let (promise, seal) = Promise<Void>.pending()
@ -96,7 +96,10 @@ public final class Poller : NSObject {
guard let envelope = SSKProtoEnvelope.from(json) else { return }
do {
let data = try envelope.serializedData()
SSKEnvironment.shared.messageReceiver.handleReceivedEnvelopeData(data)
let job = MessageReceiveJob(data: data)
Storage.write { transaction in
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
}
} catch {
print("[Loki] Failed to deserialize envelope due to error: \(error).")
}

View File

@ -105,20 +105,17 @@ public final class SessionManagementProtocol : NSObject {
// Send the session request
print("[Loki] Sending session request to: \(publicKey).")
Storage.setSessionRequestSentTimestamp(for: publicKey, to: NSDate.ows_millisecondTimeStamp(), using: transaction)
let sessionRequestMessage = SessionRequestMessage(thread: thread)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: sessionRequestMessage, transaction: transaction)
let sessionRequest = SessionRequest()
sessionRequest.preKeyBundle = storage.generatePreKeyBundle(forContact: publicKey)
MessageSender.send(sessionRequest, in: thread, using: transaction)
}
@objc(sendNullMessageToPublicKey:transaction:)
public static func sendNullMessage(to publicKey: String, in transaction: YapDatabaseReadWriteTransaction) {
let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction)
thread.save(with: transaction)
let nullMessage = OWSOutgoingNullMessage(outgoingMessageWithTimestamp: NSDate.millisecondTimestamp(), in: thread, messageBody: nil,
attachmentIds: [], expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false, groupMetaMessage: .unspecified, quotedMessage: nil,
contactShare: nil, linkPreview: nil)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: nullMessage, transaction: transaction)
let nullMessage = NullMessage()
MessageSender.send(nullMessage, in: thread, using: transaction)
}
/// - Note: Deprecated.
@ -147,9 +144,11 @@ public final class SessionManagementProtocol : NSObject {
guard ECKeyPair.isValidHexEncodedPublicKey(candidate: device) else { continue }
let thread = TSContactThread.getOrCreateThread(withContactId: device, transaction: transaction)
thread.save(with: transaction)
/*
let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread)
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
messageSenderJobQueue.add(message: endSessionMessage, transaction: transaction)
*/
}
thread.removeAllSessionRestoreDevices(with: transaction)
// Notify the user

View File

@ -101,19 +101,6 @@ public final class SessionMetaProtocol : NSObject {
return errorMessage.timestamp < restorationTimeInMs
}
@objc(shouldSkipMessageDecryptResult:wrappedIn:)
public static func shouldSkipMessageDecryptResult(_ result: OWSMessageDecryptResult, wrappedIn envelope: SSKProtoEnvelope) -> Bool {
return result.source == getUserHexEncodedPublicKey()
/*
if result.source == getUserHexEncodedPublicKey() { return true }
var isLinkedDevice = false
Storage.read { transaction in
isLinkedDevice = LokiDatabaseUtilities.isUserLinkedDevice(result.source, transaction: transaction)
}
return isLinkedDevice && envelope.type == .closedGroupCiphertext
*/
}
@objc(updateDisplayNameIfNeededForPublicKey:using:transaction:)
public static func updateDisplayNameIfNeeded(for publicKey: String, using dataMessage: SSKProtoDataMessage, in transaction: YapDatabaseReadWriteTransaction) {
guard let profile = dataMessage.profile, let displayName = profile.displayName, !displayName.isEmpty else { return }

View File

@ -28,6 +28,7 @@ public final class SyncMessagesProtocol : NSObject {
// MARK: - Sending
@objc public static func syncProfile() {
/*
Storage.writeSync { transaction in
let userPublicKey = getUserHexEncodedPublicKey()
let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userPublicKey, in: transaction)
@ -41,6 +42,7 @@ public final class SyncMessagesProtocol : NSObject {
messageSenderJobQueue.add(message: syncMessage, transaction: transaction)
}
}
*/
}
@objc(syncContactWithPublicKey:)
@ -73,6 +75,7 @@ public final class SyncMessagesProtocol : NSObject {
@objc(syncClosedGroup:transaction:)
public static func syncClosedGroup(_ thread: TSGroupThread, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise {
/*
// Prepare
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let group = thread.groupModel
@ -113,6 +116,7 @@ public final class SyncMessagesProtocol : NSObject {
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) // This internally takes care of multi device
}
sendMessageToLinkedDevices()
*/
// Return a dummy promise
return AnyPromise.from(Promise<Void> { $0.fulfill(()) })
}
@ -132,15 +136,7 @@ public final class SyncMessagesProtocol : NSObject {
}
@objc public static func syncAllOpenGroups() -> AnyPromise {
let openGroupSyncMessage = SyncOpenGroupsMessage()
let (promise, seal) = Promise<Void>.pending()
let messageSender = SSKEnvironment.shared.messageSender
messageSender.send(openGroupSyncMessage, success: {
seal.fulfill(())
}, failure: { error in
seal.reject(error)
})
return AnyPromise.from(promise)
fatalError("Not implemented.")
}
// MARK: - Receiving

View File

@ -9,6 +9,8 @@ NS_ASSUME_NONNULL_BEGIN
#ifndef TextSecureKit_Constants_h
#define TextSecureKit_Constants_h
extern const NSUInteger kOversizeTextMessageSizeThreshold;
typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
TSUnknownMessageType = 0,
TSEncryptedWhisperMessageType = 1,

View File

@ -6,6 +6,8 @@
NS_ASSUME_NONNULL_BEGIN
const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
BOOL IsUsingProductionService()
{
#ifdef USING_PRODUCTION_SERVICE

View File

@ -226,12 +226,6 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators {
self.thread = thread
}
// MARK: - Dependencies
private var messageSender: MessageSender {
return SSKEnvironment.shared.messageSender
}
// MARK: -
func didStartTypingOutgoingInput() {
@ -325,8 +319,19 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators {
if !SessionMetaProtocol.shouldSendTypingIndicator(in: thread) { return }
let message = TypingIndicatorMessage(thread: thread, action: action)
messageSender.sendPromise(message: message).retainUntilComplete()
let typingIndicator = TypingIndicator()
typingIndicator.kind = {
switch action {
case .started: return .started
case .stopped: return .stopped
}
}()
typingIndicator.threadID = thread.uniqueId!
let destination = Message.Destination.from(thread)
let job = MessageSendJob(message: typingIndicator, destination: destination)
Storage.write { transaction in
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
}
}
}

View File

@ -0,0 +1,21 @@
public extension Message.Destination {
static func from(_ thread: TSThread) -> Message.Destination {
if let thread = thread as? TSContactThread {
return .contact(publicKey: thread.uniqueId!)
} else if let thread = thread as? TSGroupThread, thread.usesSharedSenderKeys {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
return .closedGroup(groupPublicKey: groupPublicKey)
} else if let thread = thread as? TSGroupThread, thread.isPublicChat {
var openGroup: OpenGroup!
Storage.read { transaction in
openGroup = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
}
return .openGroup(channel: openGroup.channel, server: openGroup.server)
} else {
preconditionFailure("TODO: Handle legacy closed groups.")
}
}
}

View File

@ -0,0 +1,17 @@
import PromiseKit
public extension MessageSender {
static func send(_ message: Message, in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
message.threadID = thread.uniqueId!
let destination = Message.Destination.from(thread)
let job = MessageSendJob(message: message, destination: destination)
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
}
static func sendNonDurably(_ message: Message, in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
message.threadID = thread.uniqueId!
let destination = Message.Destination.from(thread)
return MessageSender.send(message, to: destination, using: transaction)
}
}