WIP
This commit is contained in:
parent
f706e38ef7
commit
15c6784f0f
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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).")
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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).")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
|
||||
|
||||
BOOL IsUsingProductionService()
|
||||
{
|
||||
#ifdef USING_PRODUCTION_SERVICE
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue