Revision 8d76e2e3

View differences:

ChatSecure.xcodeproj/project.pbxproj
179 179
		D93DDA8F1BA79A2400CD8331 /* OTRMediaFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6354BBE31A96C67400E8EBAC /* OTRMediaFileManager.m */; };
180 180
		D93DDA901BA79A2400CD8331 /* OTRMediaServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 63DDD8AF1A9D3C0400C0A918 /* OTRMediaServer.m */; };
181 181
		D93DDA911BA79A2400CD8331 /* OTRStreamManagementYapStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B916E11B743198003B778F /* OTRStreamManagementYapStorage.m */; };
182
		D93DDA921BA79A2400CD8331 /* OTRXMPPMessageYapStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 637DBB1B1B7D5A23003845B7 /* OTRXMPPMessageYapStorage.m */; };
183 182
		D93DDA931BA79A2400CD8331 /* OTRvCardYapDatabaseStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 633105341A16D1A300C17BAE /* OTRvCardYapDatabaseStorage.m */; };
184 183
		D93DDA941BA79A2400CD8331 /* OTRXMPPBuddyTimers.m in Sources */ = {isa = PBXBuildFile; fileRef = 633105361A16D1A300C17BAE /* OTRXMPPBuddyTimers.m */; };
185 184
		D93DDA951BA79A2400CD8331 /* OTRXMPPManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 633105381A16D1A300C17BAE /* OTRXMPPManager.m */; };
......
312 311
		D93DDB431BA79A7200CD8331 /* OTRMediaFileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6354BBE21A96C67400E8EBAC /* OTRMediaFileManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
313 312
		D93DDB441BA79A7200CD8331 /* OTRMediaServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 63DDD8AE1A9D3C0400C0A918 /* OTRMediaServer.h */; settings = {ATTRIBUTES = (Public, ); }; };
314 313
		D93DDB451BA79A7200CD8331 /* OTRStreamManagementYapStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 63B916E01B743198003B778F /* OTRStreamManagementYapStorage.h */; settings = {ATTRIBUTES = (Private, ); }; };
315
		D93DDB461BA79A7200CD8331 /* OTRXMPPMessageYapStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 637DBB1A1B7D5A23003845B7 /* OTRXMPPMessageYapStorage.h */; settings = {ATTRIBUTES = (Private, ); }; };
316 314
		D93DDB471BA79A7200CD8331 /* OTRvCardYapDatabaseStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 633105331A16D1A300C17BAE /* OTRvCardYapDatabaseStorage.h */; settings = {ATTRIBUTES = (Private, ); }; };
317 315
		D93DDB481BA79A7200CD8331 /* OTRXMPPBuddyTimers.h in Headers */ = {isa = PBXBuildFile; fileRef = 633105351A16D1A300C17BAE /* OTRXMPPBuddyTimers.h */; settings = {ATTRIBUTES = (Private, ); }; };
318 316
		D93DDB491BA79A7200CD8331 /* OTRXMPPManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 633105371A16D1A300C17BAE /* OTRXMPPManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
......
486 484
		D9ABD71F1ED787EE00219A9C /* OTRHTMLItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D9ABD71D1ED787EE00219A9C /* OTRHTMLItem.m */; };
487 485
		D9ABD72B1ED7886100219A9C /* OTRTextItem.h in Headers */ = {isa = PBXBuildFile; fileRef = D9ABD7291ED7886100219A9C /* OTRTextItem.h */; };
488 486
		D9ABD72C1ED7886100219A9C /* OTRTextItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D9ABD72A1ED7886100219A9C /* OTRTextItem.m */; };
487
		D9AC941A1FCD0DE50051B457 /* XMPPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AC94191FCD0DE50051B457 /* XMPPManager.swift */; };
489 488
		D9AE3A111BA8CBFA00255537 /* OTRAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = D9AE3A101BA8CBFA00255537 /* OTRAssets.h */; settings = {ATTRIBUTES = (Public, ); }; };
490 489
		D9AE3A161BA8CBFA00255537 /* OTRAssets.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D9AE3A0E1BA8CBFA00255537 /* OTRAssets.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
491 490
		D9AE3A1B1BA8CC8600255537 /* OTRSecrets.m in Sources */ = {isa = PBXBuildFile; fileRef = 633105F91A16D1A300C17BAE /* OTRSecrets.m */; };
......
1125 1124
		D9ABD71D1ED787EE00219A9C /* OTRHTMLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRHTMLItem.m; sourceTree = "<group>"; };
1126 1125
		D9ABD7291ED7886100219A9C /* OTRTextItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRTextItem.h; sourceTree = "<group>"; };
1127 1126
		D9ABD72A1ED7886100219A9C /* OTRTextItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRTextItem.m; sourceTree = "<group>"; };
1127
		D9AC94191FCD0DE50051B457 /* XMPPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPManager.swift; sourceTree = "<group>"; };
1128 1128
		D9ADE5B71B606642009471FF /* OTRCircleButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRCircleButtonView.h; sourceTree = "<group>"; };
1129 1129
		D9ADE5B81B606642009471FF /* OTRCircleButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRCircleButtonView.m; sourceTree = "<group>"; };
1130 1130
		D9AE3A0E1BA8CBFA00255537 /* OTRAssets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OTRAssets.framework; sourceTree = BUILT_PRODUCTS_DIR; };
......
1422 1422
				D9A7BCE61E4554E200888A8E /* OTRXMPPStream.m */,
1423 1423
				D9A7756D1E43F8A200027864 /* ProxyXMPPStream.h */,
1424 1424
				D9A7756E1E43F8A200027864 /* ProxyXMPPStream.m */,
1425
				D9BEF8DF1DCE6E12009945D1 /* OTRXMPPManager_Private.h */,
1426 1425
				63D639E11D12124F002B4175 /* OTRStreamManagementDelegate.swift */,
1427 1426
				633105351A16D1A300C17BAE /* OTRXMPPBuddyTimers.h */,
1428 1427
				633105361A16D1A300C17BAE /* OTRXMPPBuddyTimers.m */,
1428
				D9BEF8DF1DCE6E12009945D1 /* OTRXMPPManager_Private.h */,
1429 1429
				633105371A16D1A300C17BAE /* OTRXMPPManager.h */,
1430 1430
				633105381A16D1A300C17BAE /* OTRXMPPManager.m */,
1431
				D9AC94191FCD0DE50051B457 /* XMPPManager.swift */,
1431 1432
				636985581BC875110083FC53 /* OTRXMPPRoomManager.h */,
1432 1433
				636985591BC875110083FC53 /* OTRXMPPRoomManager.m */,
1433 1434
				6331053B1A16D1A300C17BAE /* OTRXMPPTorManager.h */,
......
2211 2212
				D93DDBA81BA79AAA00CD8331 /* OTRXMPPServerListViewController.h in Headers */,
2212 2213
				D93DDB4E1BA79A7300CD8331 /* OTRXMPPServerInfo.h in Headers */,
2213 2214
				D93DDB931BA79A9400CD8331 /* OTRXMPPPresenceSubscriptionRequest.h in Headers */,
2214
				D93DDB461BA79A7200CD8331 /* OTRXMPPMessageYapStorage.h in Headers */,
2215 2215
				D93DDB9F1BA79AA100CD8331 /* OTRXMPPError.h in Headers */,
2216 2216
				D93DDB481BA79A7200CD8331 /* OTRXMPPBuddyTimers.h in Headers */,
2217 2217
				D93DDB921BA79A9300CD8331 /* OTRXMPPBuddy.h in Headers */,
......
3093 3093
				D98BA36E1EDE47F3004475B6 /* HTMLPreviewView.swift in Sources */,
3094 3094
				D943AA421E6A0BA3007F3564 /* XMPPAccountCell.swift in Sources */,
3095 3095
				D93DDA911BA79A2400CD8331 /* OTRStreamManagementYapStorage.m in Sources */,
3096
				D93DDA921BA79A2400CD8331 /* OTRXMPPMessageYapStorage.m in Sources */,
3097 3096
				D93DDA931BA79A2400CD8331 /* OTRvCardYapDatabaseStorage.m in Sources */,
3098 3097
				D93DDA941BA79A2400CD8331 /* OTRXMPPBuddyTimers.m in Sources */,
3099 3098
				D9A7BCE81E4554E200888A8E /* OTRXMPPStream.m in Sources */,
......
3116 3115
				D93DDABC1BA79A2500CD8331 /* OTROpenInFacebookActivity.m in Sources */,
3117 3116
				D9B9B1331DC7F3AC0007F5A7 /* UserInfoProfileCell.swift in Sources */,
3118 3117
				D97070A71EEF3909004FEBDE /* MediaDownloadView.swift in Sources */,
3118
				D9AC941A1FCD0DE50051B457 /* XMPPManager.swift in Sources */,
3119 3119
				D93DDABD1BA79A2500CD8331 /* OTROpenInTwitterActivity.m in Sources */,
3120 3120
				63BAB26D1C863C60005185F3 /* PushMessage.swift in Sources */,
3121 3121
				D93DDABE1BA79A2500CD8331 /* OTRQRCodeActivity.m in Sources */,
ChatSecure/Classes/Categories/UIApplication+ChatSecure.swift
40 40
        }
41 41
    }
42 42
    
43
    @objc public func showLocalNotification(_ message:OTRMessageProtocol) {
43
    @objc public func showLocalNotification(_ message:OTRMessageProtocol, transaction: YapDatabaseReadTransaction) {
44 44
        var thread:OTRThreadOwner? = nil
45 45
        var unreadCount:UInt = 0
46 46
        var mediaItem: OTRMediaItem? = nil
47
        OTRDatabaseManager.sharedInstance().readOnlyDatabaseConnection?.read({ (transaction) -> Void in
48
            unreadCount = transaction.numberOfUnreadMessages()
49
            thread = message.threadOwner(with: transaction)
50
            mediaItem = OTRMediaItem.init(forMessage: message, transaction: transaction)
51
        })
47
        unreadCount = transaction.numberOfUnreadMessages()
48
        thread = message.threadOwner(with: transaction)
49
        mediaItem = OTRMediaItem.init(forMessage: message, transaction: transaction)
52 50
        guard let threadOwner = thread else {
53 51
            return
54 52
        }
ChatSecure/Classes/Controllers/FileTransferManager.swift
510 510
extension FileTransferManager {
511 511
    
512 512
    /** creates downloadmessages and then downloads if needed. parent message should already be saved! @warn Do not call from within an existing db transaction! */
513
    @objc public func createAndDownloadItemsIfNeeded(message: OTRMessageProtocol, readConnection: YapDatabaseConnection, force: Bool) {
514
        DispatchQueue.global(qos: .default).async {
515
            if message.messageMediaItemKey != nil || message.messageText?.count == 0 || message.downloadableURLs.count == 0 {
516
                //DDLogVerbose("Download of message not needed \(message.messageKey)")
517
                return
518
            }
519
            var downloads: [OTRDownloadMessage] = []
520
            var disableAutomaticURLFetching = false
521
            if !force {
522
                readConnection.read { (transaction) in
523
                    downloads = message.existingDownloads(with: transaction)
524
                    if let thread = message.threadOwner(with: transaction), let account = OTRAccount.fetchObject(withUniqueID: thread.threadAccountIdentifier, transaction: transaction) {
525
                        disableAutomaticURLFetching = account.disableAutomaticURLFetching
526
                    }
527
                }
513
    @objc public func createAndDownloadItemsIfNeeded(message: OTRMessageProtocol, force: Bool, transaction: YapDatabaseReadWriteTransaction) {
514
        if message.messageMediaItemKey != nil || message.messageText?.count == 0 || message.downloadableURLs.count == 0 {
515
            //DDLogVerbose("Download of message not needed \(message.messageKey)")
516
            return
517
        }
518
        var downloads: [OTRDownloadMessage] = []
519
        var disableAutomaticURLFetching = false
520
        if !force {
521
            downloads = message.existingDownloads(with: transaction)
522
            if let thread = message.threadOwner(with: transaction), let account = OTRAccount.fetchObject(withUniqueID: thread.threadAccountIdentifier, transaction: transaction) {
523
                disableAutomaticURLFetching = account.disableAutomaticURLFetching
528 524
            }
525
        }
526
        if downloads.count == 0 {
527
            downloads = message.downloads()
529 528
            if downloads.count == 0 {
530
                downloads = message.downloads()
531
                if downloads.count == 0 {
532
                    return
533
                }
534
                self.connection.readWrite({ (transaction) in
535
                    for download in downloads {
536
                        if disableAutomaticURLFetching,
537
                            let filename = download.downloadableURL?.absoluteString {
538
                            let media = OTRMediaItem.incomingItem(withFilename: filename, mimeType: nil)
539
                            media.parentObjectKey = download.uniqueId
540
                            media.parentObjectCollection = download.messageCollection
541
                            media.save(with: transaction)
542
                            download.messageMediaItemKey = media.uniqueId
543
                            download.messageError = FileTransferError.automaticDownloadsDisabled
544
                        }
545
                        download.save(with: transaction)
546
                    }
547
                    message.touch(with: transaction)
548
                })
549
            }
550
            if disableAutomaticURLFetching {
551
                DDLogVerbose("Automatic URL fetching disabled \(message.messageKey)")
552 529
                return
553 530
            }
554 531
            for download in downloads {
555
                self.downloadMediaIfNeeded(download)
532
                if disableAutomaticURLFetching,
533
                    let filename = download.downloadableURL?.absoluteString {
534
                    let media = OTRMediaItem.incomingItem(withFilename: filename, mimeType: nil)
535
                    media.parentObjectKey = download.uniqueId
536
                    media.parentObjectCollection = download.messageCollection
537
                    media.save(with: transaction)
538
                    download.messageMediaItemKey = media.uniqueId
539
                    download.messageError = FileTransferError.automaticDownloadsDisabled
540
                }
541
                download.save(with: transaction)
556 542
            }
543
            message.touch(with: transaction)
544
        }
545
        if disableAutomaticURLFetching {
546
            DDLogVerbose("Automatic URL fetching disabled \(message.messageKey)")
547
            return
548
        }
549
        for download in downloads {
550
            self.downloadMediaIfNeeded(download)
557 551
        }
558 552
    }
559 553
    
......
694 688
                } else {
695 689
                    DDLogError("Failed to refetch download message WTF \(downloadMessage)")
696 690
                }
697
            }, completionQueue: DispatchQueue.main,
698
               completionBlock: {
699
                UIApplication.shared.showLocalNotification(downloadMessage)
691
                UIApplication.shared.showLocalNotification(downloadMessage, transaction: transaction)
700 692
            })
701 693
        }, completionQueue: nil)
702 694
    }
ChatSecure/Classes/Controllers/MessageQueueHandler.swift
211 211
    fileprivate func sendDirectMessage(_ message: OTROutgoingMessage,
212 212
                                       buddy: OTRXMPPBuddy,
213 213
                                       account: OTRAccount,
214
                                       accountProtocol: OTRXMPPManager,
214
                                       accountProtocol: XMPPManager,
215 215
                                       messageSendingAction:OTRYapMessageSendAction,
216 216
                                       completion:@escaping (_ success: Bool, _ retryTimeout: TimeInterval) -> Void) {
217 217
        switch message.messageSecurity {
......
237 237
    fileprivate func sendGroupMessage(_ message: OTRXMPPRoomMessage,
238 238
                                      thread: OTRThreadOwner,
239 239
                                      account: OTRAccount,
240
                                      accountProtocol: OTRXMPPManager,
240
                                      accountProtocol: XMPPManager,
241 241
                                      messageSendingAction:OTRYapMessageSendAction,
242 242
                                      completion:@escaping (_ success: Bool, _ retryTimeout: TimeInterval) -> Void) {
243 243
        let roomManager = accountProtocol.roomManager
......
288 288
        }
289 289
        
290 290
        //Get the XMPP procol manager associated with this message and therefore account
291
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? OTRXMPPManager else {
291
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? XMPPManager else {
292 292
            completion(true, 0.0)
293 293
            return
294 294
        }
......
342 342
        }
343 343
        
344 344
        //Get the XMPP procol manager associated with this message and therefore account
345
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? OTRXMPPManager else {
345
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? XMPPManager else {
346 346
            completion(true, 0.0)
347 347
            return
348 348
        }
......
377 377
        }
378 378
        
379 379
        //Get the XMPP procol manager associated with this message and therefore account
380
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? OTRXMPPManager else {
380
        guard let accountProtocol = OTRProtocolManager.sharedInstance().protocol(for: account) as? XMPPManager else {
381 381
            completion(true, 0.0)
382 382
            return
383 383
        }
......
560 560
        }
561 561
    }
562 562
    
563
    func sendOMEMOMessage(message:OTROutgoingMessage, accountProtocol:OTRXMPPManager,completion:@escaping MessageQueueHandlerCompletion) {
563
    func sendOMEMOMessage(message:OTROutgoingMessage, accountProtocol:XMPPManager,completion:@escaping MessageQueueHandlerCompletion) {
564 564
        guard let text = message.text, text.count > 0 else {
565 565
            completion(true, 0.0)
566 566
            return
ChatSecure/Classes/Controllers/OTREncryptionManager.m
345 345
            account = [OTRAccount fetchObjectWithUniqueID:buddy.accountUniqueId transaction:transaction];
346 346
            xmpp = (OTRXMPPManager*) [[OTRProtocolManager sharedInstance] protocolForAccount:account];
347 347
            [xmpp sendDeliveryReceiptForMessage:originalMessage];
348
        } completionBlock:^{
349
            [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:originalMessage readConnection:OTRDatabaseManager.shared.readOnlyDatabaseConnection force:NO];
350
            [[UIApplication sharedApplication] showLocalNotification:originalMessage];
348
            
349
            [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:originalMessage force:NO transaction:transaction];
350
            [[UIApplication sharedApplication] showLocalNotification:originalMessage transaction:transaction];
351 351
        }];
352 352
    }
353 353
    
ChatSecure/Classes/Controllers/OTROMEMOSignalCoordinator.swift
27 27
    @objc open weak var omemoModuleQueue:DispatchQueue?
28 28
    @objc open var callbackQueue:DispatchQueue
29 29
    @objc open let workQueue:DispatchQueue
30
    @objc open let messageStorage: MessageStorage
31
    
30 32
    fileprivate var myJID:XMPPJID? {
31 33
        get {
32 34
            return omemoModule?.xmppStream?.myJID
......
40 42
     - parameter accountYapKey: The accounts unique yap key
41 43
     - parameter databaseConnection: A yap database connection on which all operations will be completed on
42 44
 `  */
43
    @objc public required init(accountYapKey:String,  databaseConnection:YapDatabaseConnection) throws {
45
    @objc public required init(accountYapKey:String,  databaseConnection:YapDatabaseConnection,
46
                               messageStorage: MessageStorage) throws {
44 47
        try self.signalEncryptionManager = OTRAccountSignalEncryptionManager(accountKey: accountYapKey,databaseConnection: databaseConnection)
45 48
        self.omemoStorageManager = OTROMEMOStorageManager(accountKey: accountYapKey, accountCollection: OTRAccount.collection, databaseConnection: databaseConnection)
46 49
        self.accountYapKey = accountYapKey
......
48 51
        self.outstandingXMPPStanzaResponseBlocks = [:]
49 52
        self.callbackQueue = DispatchQueue(label: "OTROMEMOSignalCoordinator-callback", attributes: [])
50 53
        self.workQueue = DispatchQueue(label: "OTROMEMOSignalCoordinator-work", attributes: [])
54
        self.messageStorage = messageStorage
51 55
    }
52 56
    
53 57
    /**
......
355 359
        }
356 360
    }
357 361
    
358
    open func processKeyData(_ keyData: [OMEMOKeyData], iv: Data, senderDeviceId: UInt32, fromJID: XMPPJID, payload: Data?, message: XMPPMessage) {
362
    open func processKeyData(_ keyData: [OMEMOKeyData], iv: Data, senderDeviceId: UInt32, forJID: XMPPJID, payload: Data?, delayed: Date?, forwarded: Bool, isIncoming: Bool, message: XMPPMessage) {
359 363
        let aesGcmBlockLength = 16
360
        guard let encryptedPayload = payload, encryptedPayload.count > 0 else {
364
        guard let encryptedPayload = payload, encryptedPayload.count > 0, let myJID = self.myJID else {
361 365
            return
362 366
        }
363
        
367
        var addressJID = forJID.bareJID
368
        if !isIncoming {
369
            addressJID = myJID.bareJID
370
        }
364 371
        let rid = self.signalEncryptionManager.registrationId
365 372
        
366 373
        //Could have multiple matching device id. This is extremely rare but possible that the sender has another device that collides with our device id.
367 374
        var unencryptedKeyData: Data?
368
        for key in keyData {
369
            if key.deviceId == rid {
370
                let keyData = key.data
371
                do {
372
                    unencryptedKeyData = try self.signalEncryptionManager.decryptFromAddress(keyData, name: fromJID.bare, deviceId: senderDeviceId)
373
                    // have successfully decripted the AES key. We should break and use it to decrypt the payload
374
                    break
375
                } catch let error {
376
                    DDLogError("Error decrypting: \(error)")
377
                    let nsError = error as NSError
378
                    if nsError.domain == SignalErrorDomain, nsError.code == SignalError.duplicateMessage.rawValue {
379
                        // duplicate messages are benign and can be ignored
380
                        return
381
                    }
382
                    let buddyAddress = SignalAddress(name: fromJID.bare, deviceId: Int32(senderDeviceId))
383
                    if self.signalEncryptionManager.storage.sessionRecordExists(for: buddyAddress) {
384
                        // Session is corrupted
385
                        let _ = self.signalEncryptionManager.storage.deleteSessionRecord(for: buddyAddress)
386
                        DDLogError("Session exists and is possibly corrupted. Deleting...")
387
                    }
375
        for key in keyData where key.deviceId == rid {
376
            let keyData = key.data
377
            do {
378
                unencryptedKeyData = try self.signalEncryptionManager.decryptFromAddress(keyData, name: addressJID.bare, deviceId: senderDeviceId)
379
                // have successfully decripted the AES key. We should break and use it to decrypt the payload
380
                break
381
            } catch let error {
382
                DDLogError("Error decrypting OMEMO message for \(addressJID): \(error) \(message)")
383
                let nsError = error as NSError
384
                if nsError.domain == SignalErrorDomain, nsError.code == SignalError.duplicateMessage.rawValue {
385
                    // duplicate messages are benign and can be ignored
386
                    DDLogInfo("Ignoring duplicate OMEMO message: \(message)")
388 387
                    return
389 388
                }
389
                let buddyAddress = SignalAddress(name: addressJID.bare, deviceId: Int32(senderDeviceId))
390
                if self.signalEncryptionManager.storage.sessionRecordExists(for: buddyAddress) {
391
                    // Session is corrupted
392
                    let _ = self.signalEncryptionManager.storage.deleteSessionRecord(for: buddyAddress)
393
                    DDLogError("Session exists and is possibly corrupted. Deleting...")
394
                }
395
                return
390 396
            }
391 397
        }
392 398
        
......
418 424
        }
419 425
        
420 426
        do {
421
            guard let messageBody = try OTRSignalEncryptionHelper.decryptData(encryptedBody, key: aesKey, iv: iv, authTag: tag) else {
422
                return
423
            }
424
            let messageString = String(data: messageBody, encoding: String.Encoding.utf8)
425
            var databaseMessage:OTRBaseMessage = OTRIncomingMessage()
426
            guard let ourJID = self.myJID else {
427
            guard let messageBody = try OTRSignalEncryptionHelper.decryptData(encryptedBody, key: aesKey, iv: iv, authTag: tag),
428
            let messageString = String(data: messageBody, encoding: String.Encoding.utf8),
429
            messageString.count > 0 else {
427 430
                return
428 431
            }
429
            var relatedBuddyUsername: String? = fromJID.bare
430
            var innerMessage = message
431
            if message.isTrustedMessageCarbon(forMyJID: ourJID),
432
                let forwarded = message.messageCarbonForwarded {
433
                //This came from another of our devices this is really going to be an outgoing message
434
                innerMessage = forwarded
435
                if (message.isReceivedMessageCarbon) {
436
                    relatedBuddyUsername = innerMessage.from?.bare
437
                } else {
438
                    relatedBuddyUsername = innerMessage.to?.bare
439
                    let outgoingMessage = OTROutgoingMessage()!
440
                    outgoingMessage.dateSent = Date()
441
                    databaseMessage = outgoingMessage
442
                }
443
            }
444
            
445
            var xmpp: OTRXMPPManager? = nil
446
            var account: OTRAccount? = nil
447 432
            
448
            self.databaseConnection.asyncReadWrite({ (transaction) in
449
                
450
                guard let buddyUsernmae = relatedBuddyUsername, let jid = XMPPJID(string: buddyUsernmae), let buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: self.accountYapKey, transaction: transaction) else {
433
            let preSave: MessageStorage.PreSave = { message, transaction in
434
                guard let buddy = OTRXMPPBuddy.fetchBuddy(jid: forJID, accountUniqueId: self.accountYapKey, transaction: transaction) else {
451 435
                    return
452 436
                }
453
                databaseMessage.text = messageString
454
                databaseMessage.buddyUniqueId = buddy.uniqueId
455
                
437

  
456 438
                let deviceNumber = NSNumber(value: senderDeviceId as UInt32)
457 439
                let deviceYapKey = OTROMEMODevice.yapKey(withDeviceId: deviceNumber, parentKey: buddy.uniqueId, parentCollection: OTRBuddy.collection)
458
                databaseMessage.messageSecurityInfo = OTRMessageEncryptionInfo.init(omemoDevice: deviceYapKey, collection: OTROMEMODevice.collection)
459
                if let id = innerMessage.elementID {
460
                    databaseMessage.messageId = id
461
                }
440
                message.messageSecurityInfo = OTRMessageEncryptionInfo.init(omemoDevice: deviceYapKey, collection: OTROMEMODevice.collection)
462 441
                
463
                databaseMessage.save(with: transaction)
442
                message.save(with: transaction)
464 443
                
465 444
                // Should we be using the date of the xmpp message?
466
                buddy.lastMessageId = databaseMessage.uniqueId
445
                buddy.lastMessageId = message.uniqueId
467 446
                buddy.save(with: transaction)
468 447
                
469 448
                //Update device last received message
......
472 451
                }
473 452
                let newDevice = OTROMEMODevice(deviceId: device.deviceId, trustLevel: device.trustLevel, parentKey: device.parentKey, parentCollection: device.parentCollection, publicIdentityKeyData: device.publicIdentityKeyData, lastSeenDate: Date())
474 453
                newDevice.save(with: transaction)
475
                
476
                // Send delivery receipt
477
                account = OTRAccount.fetchObject(withUniqueID: buddy.accountUniqueId, transaction: transaction)
478
                if let account = account {
479
                    xmpp = OTRProtocolManager.sharedInstance().protocol(for: account) as? OTRXMPPManager
480
                } else {
481
                    return
482
                }
483
                
484
                if let incomingMessage = databaseMessage as? OTRIncomingMessage {
485
                    xmpp?.sendDeliveryReceipt(for: incomingMessage)
486
                }
487
                
488
                }, completionQueue: DispatchQueue.main,
489
                   completionBlock: {
490
                    guard let _ = databaseMessage.text else {
491
                        return
492
                    }
493
                    
494
                    xmpp?.fileTransferManager.createAndDownloadItemsIfNeeded(message: databaseMessage, readConnection: OTRDatabaseManager.shared.readOnlyDatabaseConnection ?? self.databaseConnection, force: false)
495
                    UIApplication.shared.showLocalNotification(databaseMessage)
496
            })
497
            // Display local notification
454
            }
498 455
            
499
        } catch {
456
            if forwarded {
457
                self.messageStorage.handleForwardedMessage(message, forJID: forJID, body: messageString, accountId: self.accountYapKey, delayed: delayed, isIncoming: isIncoming, preSave: preSave)
458
            } else {
459
                self.messageStorage.handleDirectMessage(message, body: messageString, accountId: self.accountYapKey, preSave: preSave)
460
            }
461
        } catch let error {
462
            DDLogError("Message decryption error: \(error)")
500 463
            return
501 464
        }
502

  
503 465
    }
504 466
}
505 467

  
......
595 557
    }
596 558
    
597 559
    public func omemo(_ omemo: OMEMOModule, receivedKeyData keyData: [OMEMOKeyData], iv: Data, senderDeviceId: UInt32, from fromJID: XMPPJID, payload: Data?, message: XMPPMessage) {
598
        self.processKeyData(keyData, iv: iv, senderDeviceId: senderDeviceId, fromJID: fromJID, payload: payload, message: message)
560
        self.processKeyData(keyData, iv: iv, senderDeviceId: senderDeviceId, forJID: fromJID, payload: payload, delayed: nil, forwarded: false, isIncoming: true, message: message)
561
    }
562
    
563
    public func omemo(_ omemo: OMEMOModule, receivedForwardedKeyData keyData: [OMEMOKeyData], iv: Data, senderDeviceId: UInt32, for forJID: XMPPJID, payload: Data?, isIncoming: Bool, delayed: Date?, forwardedMessage: XMPPMessage, originalMessage: XMPPMessage) {
564
        self.processKeyData(keyData, iv: iv, senderDeviceId: senderDeviceId, forJID: forJID, payload: payload, delayed: delayed, forwarded: true, isIncoming: isIncoming, message: forwardedMessage)
599 565
    }
600 566
}
601 567

  
ChatSecure/Classes/Controllers/OTROMEMOStorageManager.swift
157 157
    open func storeBuddyDevices(_ devices:[NSNumber], buddyUsername:String, completion:(()->Void)?) {
158 158
        self.databaseConnection.asyncReadWrite { (transaction) in
159 159
            // Fetch the buddy from the database.
160
            var buddy: OTRXMPPBuddy? = nil
161
            if let jid = XMPPJID(string: buddyUsername) {
162
                buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: self.accountKey, transaction: transaction)
160
            guard let jid = XMPPJID(string: buddyUsername), let buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: self.accountKey, transaction: transaction) else {
161
                // If this is teh first launch the buddy will not be in the buddy list becuase the roster comes in after device list from PEP.
162
                DDLogWarn("Could not find buddy to store devices \(buddyUsername)")
163
                return
163 164
            }
164
            // If this is teh first launch the buddy will not be in the buddy list becuase the roster comes in after device list from PEP.
165
            // So we create a buddy witht the minimial information we have in order to save the device list.
166
            if (buddy == nil) {
167
                buddy = OTRXMPPBuddy()
168
                buddy?.username = buddyUsername
169
                buddy?.accountUniqueId = self.accountKey
170
                buddy?.save(with: transaction)
171
            }
172
            if let bud = buddy {
173
                self.storeDevices(devices, parentYapKey: bud.uniqueId, parentYapCollection: type(of: bud).collection, transaction: transaction)
174
                if let completion = completion {
175
                    completion()
176
                }
177
            }
178
            
165
            self.storeDevices(devices, parentYapKey: buddy.uniqueId, parentYapCollection: buddy.threadCollection, transaction: transaction)
166
            completion?()
179 167
        }
180 168
    }
181 169
}
ChatSecure/Classes/Controllers/ServerCheck.swift
17 17
 */
18 18
public class ServerCheck: NSObject, OTRServerCapabilitiesDelegate, XMPPPushDelegate {
19 19
    
20
    @objc public weak var xmpp: OTRXMPPManager?
20
    @objc public weak var xmpp: XMPPManager?
21 21
    @objc public let push: PushController
22 22
    
23 23
    @objc public var result = ServerCheckResult()
......
31 31
    }
32 32
    
33 33
    
34
    @objc public init(xmpp: OTRXMPPManager, push: PushController) {
34
    @objc public init(xmpp: XMPPManager, push: PushController) {
35 35
        self.push = push
36 36
        self.xmpp = xmpp
37 37
        super.init()
ChatSecure/Classes/Controllers/XMPP/OTRXMPPManager.h
33 33

  
34 34
@class OTRXMPPAccount;
35 35
@class OTROMEMOSignalCoordinator;
36
@class XMPPPushModule, ServerCheck, FileTransferManager;
36
@class XMPPPushModule, ServerCheck, FileTransferManager, MessageStorage;
37 37

  
38 38
NS_ASSUME_NONNULL_BEGIN
39
NS_SWIFT_NAME(XMPPManager)
39 40
@interface OTRXMPPManager : NSObject <XMPPRosterDelegate, XMPPStreamDelegate, NSFetchedResultsControllerDelegate, OTRProtocol>
40 41

  
41 42
@property (nonatomic, strong, readonly) OTRXMPPAccount *account;
......
48 49
@property (nonatomic, strong, readonly) XMPPPushModule *xmppPushModule;
49 50
@property (nonatomic, strong, readonly) ServerCheck *serverCheck;
50 51
@property (nonatomic, strong, readonly) FileTransferManager *fileTransferManager;
52
@property (nonatomic, strong, readonly) YapDatabaseConnection *databaseConnection;
53
@property (nonatomic, strong, readonly) MessageStorage *messageStorage;
54

  
51 55

  
52 56
/** Useful for showing error messages related to connection, like SSL certs. Only safe for access from main queue. */
53 57
@property (nonatomic, readonly, nullable) NSError *lastConnectionError;
ChatSecure/Classes/Controllers/XMPP/OTRXMPPManager.m
242 242
    _fileTransferManager = [[FileTransferManager alloc] initWithConnection:self.databaseConnection serverCapabilities:self.serverCapabilities sessionConfiguration:sessionConfiguration];
243 243
    
244 244
    // Message storage
245
    _messageStorage = [[MessageStorage alloc] initWithConnection:self.databaseConnection capabilities:self.xmppCapabilities dispatchQueue:nil];
245
    _messageStorage = [[MessageStorage alloc] initWithConnection:self.databaseConnection capabilities:self.xmppCapabilities fileTransfer:self.fileTransferManager dispatchQueue:nil];
246 246
    [self.messageStorage activate:self.xmppStream];
247 247
    
248 248
    //Stream Management
......
274 274
    
275 275
    //OMEMO
276 276
    if ([[OTRAppDelegate appDelegate].theme enableOMEMO]) {
277
        self.omemoSignalCoordinator = [[OTROMEMOSignalCoordinator alloc] initWithAccountYapKey:self.account.uniqueId databaseConnection:self.databaseConnection error:nil];
277
        self.omemoSignalCoordinator = [[OTROMEMOSignalCoordinator alloc] initWithAccountYapKey:self.account.uniqueId databaseConnection:self.databaseConnection messageStorage:self.messageStorage error:nil];
278 278
        _omemoModule = [[OMEMOModule alloc] initWithOMEMOStorage:self.omemoSignalCoordinator xmlNamespace:OMEMOModuleNamespaceConversationsLegacy];
279 279
        [self.omemoModule addDelegate:self.omemoSignalCoordinator delegateQueue:self.workQueue];
280 280
        [self.omemoModule activate:self.xmppStream];
......
880 880
    // Fetch latest vCard from server so we can update nickname
881 881
    //[self.xmppvCardTempModule fetchvCardTempForJID:self.JID ignoreStorage:YES];
882 882
    
883
    // Testing MAM
884
    [self.messageStorage.archiving retrieveMessageArchiveWithFields:nil withResultSet:nil];
883
    // [self.messageStorage.archiving retrieveMessageArchiveWithFields:nil withResultSet:nil];
885 884
}
886 885

  
887 886
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
ChatSecure/Classes/Controllers/XMPP/OTRXMPPManager_Private.h
8 8

  
9 9
#import "OTRXMPPManager.h"
10 10
#import "OTRCertificatePinning.h"
11
#import "OTRXMPPMessageYapStorage.h"
12 11
#import "OTRXMPPBuddyManager.h"
13 12
#import <ChatSecureCore/ChatSecureCore-Swift.h>
14 13
#import "OTRYapDatabaseRosterStorage.h"
......
29 28

  
30 29
@property (nonatomic, strong, readonly) XMPPStreamManagement *streamManagement;
31 30

  
32
@property (nonatomic, strong, readonly) MessageStorage *messageStorage;
33 31
@property (nonatomic, strong, readonly) OTRXMPPBuddyManager* xmppBuddyManager;
34 32
@property (nonatomic, strong, readonly) OMEMOModule *omemoModule;
35 33
@property (nonatomic, strong, nullable) OTRXMPPChangePasswordManager *changePasswordManager;
36 34

  
37
@property (nonatomic, strong, readonly) YapDatabaseConnection *databaseConnection;
38 35
@property (nonatomic, strong, readonly) XMPPMessageDeliveryReceipts *deliveryReceipts;
39 36
@property (nonatomic, strong, readonly) OTRXMPPMessageStatusModule *messageStatusModule;
40 37
@property (nonatomic, strong, readonly) OTRStreamManagementDelegate *streamManagementDelegate;
ChatSecure/Classes/Controllers/XMPP/Storage/MessageStorage.swift
10 10
import XMPPFramework
11 11
import CocoaLumberjack
12 12

  
13

  
13 14
@objc public class MessageStorage: XMPPModule {
15
    /// This gets called before a message is saved, if additional processing needs to be done elsewhere
16
    public typealias PreSave = (_ message: OTRBaseMessage, _ transaction: YapDatabaseReadWriteTransaction) -> Void
17

  
14 18
    // MARK: Properties
15 19
    private let connection: YapDatabaseConnection
16 20
    
......
18 22
    @objc public let capabilities: XMPPCapabilities
19 23
    @objc public let carbons: XMPPMessageCarbons
20 24
    @objc public let archiving: XMPPMessageArchiveManagement
25
    @objc public let fileTransfer: FileTransferManager
21 26

  
22 27
    // MARK: Init
23 28
    deinit {
......
28 33
    /// Capabilities must be activated elsewhere
29 34
    @objc public init(connection: YapDatabaseConnection,
30 35
                      capabilities: XMPPCapabilities,
36
                      fileTransfer: FileTransferManager,
31 37
                      dispatchQueue: DispatchQueue? = nil) {
32 38
        self.connection = connection
33 39
        self.capabilities = capabilities
34 40
        self.carbons = XMPPMessageCarbons(dispatchQueue: dispatchQueue)
35 41
        self.archiving = XMPPMessageArchiveManagement(dispatchQueue: dispatchQueue)
42
        self.fileTransfer = fileTransfer
36 43
        super.init(dispatchQueue: dispatchQueue)
37 44
        self.carbons.addDelegate(self, delegateQueue: self.moduleQueue)
38 45
        self.archiving.addDelegate(self, delegateQueue: self.moduleQueue)
......
64 71
        OTRBuddyCache.shared.setChatState(chatState, for: buddy)
65 72
    }
66 73
    
67
    /// Marks a previously sent outgoing message as delivered
74
    /// Marks a previously sent outgoing message as delivered.
68 75
    private func handleDeliveryResponse(message: XMPPMessage, transaction: YapDatabaseReadWriteTransaction) {
69 76
        guard message.hasReceiptResponse,
70 77
            !message.isErrorMessage,
71 78
            let responseId = message.receiptResponseID else {
72 79
                return
73 80
        }
74
        OTROutgoingMessage.receivedDeliveryReceipt(forMessageId: responseId, transaction: transaction)
81
        var _deliveredMessage: OTROutgoingMessage? = nil
82
        transaction.enumerateMessages(elementId: responseId, originId: responseId, stanzaId: nil) { (message, stop) in
83
            if let message = message as? OTROutgoingMessage {
84
                _deliveredMessage = message
85
                stop.pointee = true
86
            }
87
        }
88
        if _deliveredMessage == nil {
89
            DDLogVerbose("Outgoing message not found for receipt: \(message)")
90
            // This can happen with MAM + OMEMO where the decryption
91
            // for the OMEMO message makes it come in after the receipt
92
            // To solve this, we need to make a placeholder message...
93
            
94
            // TODO.......
95
        }
96
        guard let deliveredMessage = _deliveredMessage,
97
            deliveredMessage.isDelivered == false,
98
            deliveredMessage.dateDelivered == nil else {
99
            return
100
        }
101
        if let deliveredMessage = deliveredMessage.copyAsSelf() {
102
            deliveredMessage.isDelivered = true
103
            deliveredMessage.dateDelivered = Date()
104
            deliveredMessage.save(with: transaction)
105
        }        
75 106
    }
76 107
    
77 108
    /// It is a violation of the XMPP spec to discard messages with duplicate stanza elementIds. We must use XEP-0359 stanza-id only.
......
87 118
    }
88 119
    
89 120
    /// Handles both MAM and Carbons
90
    private func handleForwardedMessage(_ xmppMessage: XMPPMessage,
121
    public func handleForwardedMessage(_ xmppMessage: XMPPMessage,
122
                                        forJID: XMPPJID,
123
                                        body: String?,
91 124
                                        accountId: String,
92 125
                                        delayed: Date?,
93
                                        isIncoming: Bool) {
94
        guard xmppMessage.isMessageWithBody,
95
            !xmppMessage.isErrorMessage,
96
            let messageBody = xmppMessage.body,
97
            OTRKit.stringStarts(withOTRPrefix: messageBody) else {
126
                                        isIncoming: Bool,
127
                                        preSave: PreSave? = nil ) {
128
        guard !xmppMessage.isErrorMessage else {
98 129
            DDLogWarn("Discarding forwarded message: \(xmppMessage)")
99 130
            return
100 131
        }
101
        
102
        //Sent Message Carbons are sent by our account to another
103
        //So from is our JID and to is buddy
104
        var _jid: XMPPJID? = nil
105
        if isIncoming {
106
            _jid = xmppMessage.from
107
        } else {
108
            _jid = xmppMessage.to
109
        }
110
        guard let jid = _jid else {
132
        // Ignore OTR text
133
        if let messageBody = xmppMessage.body, messageBody.isOtrText {
111 134
            return
112 135
        }
113
        
136

  
114 137
        connection.asyncReadWrite { (transaction) in
115 138
            guard let account = OTRXMPPAccount.fetchObject(withUniqueID: accountId, transaction: transaction),
116
                let buddy = OTRXMPPBuddy.fetchBuddy(jid: jid, accountUniqueId: accountId, transaction: transaction) else {
139
                let buddy = OTRXMPPBuddy.fetchBuddy(jid: forJID, accountUniqueId: accountId, transaction: transaction) else {
117 140
                    return
118 141
            }
119
            var message: OTRBaseMessage? = nil
142
            var _message: OTRBaseMessage? = nil
143
            
120 144
            if isIncoming {
121
                self.handleChatState(message: xmppMessage, buddy: buddy)
122 145
                self.handleDeliveryResponse(message: xmppMessage, transaction: transaction)
123
                message = OTRIncomingMessage(xmppMessage: xmppMessage, account: account, buddy: buddy, capabilities: self.capabilities)
146
                self.handleChatState(message: xmppMessage, buddy: buddy)
147
                _message = OTRIncomingMessage(xmppMessage: xmppMessage, body: body, account: account, buddy: buddy, capabilities: self.capabilities)
124 148
            } else {
125
                message = OTROutgoingMessage(xmppMessage: xmppMessage, account: account, buddy: buddy, capabilities: self.capabilities)
149
                let outgoing = OTROutgoingMessage(xmppMessage: xmppMessage, body: body, account: account, buddy: buddy, capabilities: self.capabilities)
150
                outgoing.dateSent = delayed ?? Date()
151
                _message = outgoing
126 152
            }
127
            guard let stanzaId = message?.stanzaId,
128
                !self.isDuplicate(xmppMessage: xmppMessage, stanzaId: stanzaId, buddyUniqueId: buddy.uniqueId, transaction: transaction) else {
129
                    DDLogWarn("Duplicate forwarded message received: \(xmppMessage)")
130
                    return
153
            guard let message = _message else {
154
                DDLogWarn("Discarding empty message: \(xmppMessage)")
155
                return
156
            }
157
            
158
            // Bail out if we receive duplicate messages identified by XEP-0359
159
            if let stanzaId = message.stanzaId,
160
            self.isDuplicate(xmppMessage: xmppMessage, stanzaId: stanzaId, buddyUniqueId: buddy.uniqueId, transaction: transaction) {
161
                DDLogWarn("Duplicate forwarded message received: \(xmppMessage)")
162
                return
131 163
            }
164
            
132 165
            if let delayed = delayed {
133
                message?.date = delayed
166
                message.date = delayed
167
            }
168
            preSave?(message, transaction)
169
            message.save(with: transaction)
170
            if let incoming = message as? OTRIncomingMessage {
171
                self.finishHandlingIncomingMessage(incoming, account: account, transaction: transaction)
134 172
            }
135
            message?.save(with: transaction)
136 173
        }
137 174
    }
138 175
    
139 176
    /// Inserts direct message into database
140
    private func handleDirectMessage(_ message: XMPPMessage, accountId: String) {
141
        connection.asyncReadWrite { (transaction) in
177
    public func handleDirectMessage(_ message: XMPPMessage,
178
                                    body: String?,
179
                                    accountId: String,
180
                                    preSave: PreSave? = nil) {
181
        var incomingMessage: OTRIncomingMessage? = nil
182
        connection.asyncReadWrite({ (transaction) in
142 183
            guard let account = OTRXMPPAccount.fetchObject(withUniqueID: accountId, transaction: transaction),
143 184
                let fromJID = message.from,
144 185
                let buddy = OTRXMPPBuddy.fetchBuddy(jid: fromJID, accountUniqueId: accountId, transaction: transaction)
145 186
                else {
146 187
                    return
147 188
            }
148
            
189

  
149 190
            // Update ChatState
150 191
            self.handleChatState(message: message, buddy: buddy)
151 192
            
......
180 221
                return
181 222
            }
182 223
            
183
            let incoming = OTRIncomingMessage(xmppMessage: message, account: account, buddy: buddy, capabilities: self.capabilities)
224
            incomingMessage = OTRIncomingMessage(xmppMessage: message, body: body, account: account, buddy: buddy, capabilities: self.capabilities)
184 225
            
185 226
            // Check for duplicates
186
            if let stanzaId = incoming.stanzaId,
227
            if let stanzaId = incomingMessage?.stanzaId,
187 228
                self.isDuplicate(xmppMessage: message, stanzaId: stanzaId, buddyUniqueId: buddy.uniqueId, transaction: transaction) {
188 229
                DDLogWarn("Duplicate message received: \(message)")
189 230
                return
190 231
            }
232
            guard let incoming = incomingMessage, let text = incoming.text, text.count > 0 else {
233
                // discard empty message text
234
                return
235
            }
191 236
            
192
            // TODO: Replace this so we aren't passing everything through OTRKit
193
            if let text = incoming.text {
237
            if text.isOtrText {
194 238
                OTRProtocolManager.shared.encryptionManager.otrKit.decodeMessage(text, username: buddy.username, accountName: account.username, protocol: kOTRProtocolTypeXMPP, tag: incoming)
239
            } else {
240
                preSave?(incoming, transaction)
241
                incoming.save(with: transaction)
242
                self.finishHandlingIncomingMessage(incoming, account: account, transaction: transaction)
195 243
            }
244
        })
245
    }
246
    
247
    private func finishHandlingIncomingMessage(_ message: OTRIncomingMessage, account: OTRXMPPAccount, transaction: YapDatabaseReadWriteTransaction) {
248
        guard let xmpp = OTRProtocolManager.shared.protocol(for: account) as? XMPPManager else {
249
            return
196 250
        }
251
        xmpp.sendDeliveryReceipt(for: message)
252
        
253
        self.fileTransfer.createAndDownloadItemsIfNeeded(message: message, force: false, transaction: transaction)
254
        UIApplication.shared.showLocalNotification(message, transaction: transaction)
197 255
    }
198 256
}
199 257

  
......
210 268
            !message.isMessageCarbon,
211 269
            // We handle MAM elsewhere as well
212 270
            message.mamResult == nil,
271
            // OMEMO messages cannot be processed here
272
            !message.omemo_hasEncryptedElement(.conversationsLegacy),
213 273
            let accountId = sender.accountId else {
214 274
            return
215 275
        }
216 276
        
217
        handleDirectMessage(message, accountId: accountId)
277
        handleDirectMessage(message, body: nil, accountId: accountId)
218 278
    }
219 279
}
220 280

  
221 281
extension MessageStorage: XMPPMessageCarbonsDelegate {
222 282

  
223 283
    public func xmppMessageCarbons(_ xmppMessageCarbons: XMPPMessageCarbons, didReceive message: XMPPMessage, outgoing isOutgoing: Bool) {
224
        guard let accountId = xmppMessageCarbons.xmppStream?.accountId else {
284
        guard let accountId = xmppMessageCarbons.xmppStream?.accountId,
285
        !message.omemo_hasEncryptedElement(.conversationsLegacy) else {
225 286
            return
226 287
        }
227
        handleForwardedMessage(message, accountId: accountId, delayed: nil, isIncoming: !isOutgoing)
288
        var _forJID: XMPPJID? = nil
289
        if !isOutgoing {
290
            _forJID = message.from
291
        } else {
292
            _forJID = message.to
293
        }
294
        guard let forJID = _forJID else { return }
295
        handleForwardedMessage(message, forJID: forJID, body: nil, accountId: accountId, delayed: nil, isIncoming: !isOutgoing)
228 296
    }
229 297
}
230 298

  
231 299
extension MessageStorage: XMPPMessageArchiveManagementDelegate {
300
    public func xmppMessageArchiveManagement(_ xmppMessageArchiveManagement: XMPPMessageArchiveManagement, didFailToReceiveMessages error: XMPPIQ) {
301
        DDLogError("Failed to receive messages \(error)")
302
    }
303
    
232 304
    public func xmppMessageArchiveManagement(_ xmppMessageArchiveManagement: XMPPMessageArchiveManagement, didReceiveMAMMessage message: XMPPMessage) {
233 305
        guard let accountId = xmppMessageArchiveManagement.xmppStream?.accountId,
234 306
            let myJID = xmppMessageArchiveManagement.xmppStream?.myJID,
235 307
            let result = message.mamResult,
236 308
            let forwarded = result.forwardedMessage,
237
            let delayed = result.forwardedStanzaDelayedDeliveryDate,
238
            let from = forwarded.from else {
309
            let from = forwarded.from,
310
            !forwarded.omemo_hasEncryptedElement(.conversationsLegacy) else {
311
                DDLogVerbose("Discarding incoming MAM message \(message)")
239 312
                return
240 313
        }
314
        let delayed = result.forwardedStanzaDelayedDeliveryDate
241 315
        let isIncoming = !from.isEqual(to: myJID, options: .bare)
242
        handleForwardedMessage(forwarded, accountId: accountId, delayed: delayed, isIncoming: isIncoming)
316
        var _forJID: XMPPJID? = nil
317
        if isIncoming {
318
            _forJID = forwarded.from
319
        } else {
320
            _forJID = forwarded.to
321
        }
322
        guard let forJID = _forJID else { return }
323
        handleForwardedMessage(forwarded, forJID: forJID, body: nil, accountId: accountId, delayed: delayed, isIncoming: isIncoming)
243 324
    }
244 325
}
245 326

  
......
274 355
    }
275 356
}
276 357

  
358
extension String {
359
    /// https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html
360
    static let OTRWhitespaceStart = String(bytes: [0x20,0x09,0x20,0x20,0x09,0x09,0x09,0x09,0x20,0x09,0x20,0x09,0x20,0x09,0x20,0x20], encoding: .utf8)!
361
    
362
    /// for separately handling OTR messages
363
    var isOtrText: Bool {
364
        return self.contains("?OTR") || self.contains(String.OTRWhitespaceStart)
365
    }
366
}
367

  
277 368
extension OTRBaseMessage {
278
    convenience init(xmppMessage: XMPPMessage, account: OTRXMPPAccount, buddy: OTRXMPPBuddy, capabilities: XMPPCapabilities) {
369
    /// You can override message body, for example if this is an encrypted message
370
    convenience init(xmppMessage: XMPPMessage, body: String?, account: OTRXMPPAccount, buddy: OTRXMPPBuddy, capabilities: XMPPCapabilities) {
279 371
        self.init()
280
        self.messageText = xmppMessage.body
372
        self.messageText = body ?? xmppMessage.body
281 373
        self.buddyUniqueId = buddy.uniqueId
282 374
        if let delayed = xmppMessage.delayedDeliveryDate {
283 375
            self.messageDate = delayed
......
302 394
        }
303 395
    }
304 396
}
397

  
398
extension NSCopying {
399
    /// Creates a deep copy of the object
400
    func copyAsSelf() -> Self? {
401
        return self.copy() as? Self
402
    }
403
}
404

  
ChatSecure/Classes/Controllers/XMPP/Storage/OTRXMPPRoomYapStorage.m
129 129
        
130 130
        [databaseRoom saveWithTransaction:transaction];
131 131
        [databaseMessage saveWithTransaction:transaction];
132
    } completionBlock:^{
132
        
133 133
        if(databaseMessage) {
134 134
            OTRXMPPManager *xmpp = (OTRXMPPManager*)[OTRProtocolManager.shared protocolForAccount:account];
135
            [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:databaseMessage readConnection:OTRDatabaseManager.shared.readOnlyDatabaseConnection force:NO];
135
            [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:databaseMessage force:NO transaction:transaction];
136 136
            // If delayedDeliveryDate is set we are retrieving history. Don't show
137 137
            // notifications in that case. Also, don't show notifications for archived
138 138
            // rooms.
139 139
            if (!message.delayedDeliveryDate && !databaseRoom.isArchived) {
140
                [[UIApplication sharedApplication] showLocalNotification:databaseMessage];
140
                [[UIApplication sharedApplication] showLocalNotification:databaseMessage transaction:transaction];
141 141
            }
142 142
        }
143 143
    }];
ChatSecure/Classes/Controllers/XMPP/Storage/OTRYapDatabaseRosterStorage.m
21 21

  
22 22
@interface OTRYapDatabaseRosterStorage ()
23 23

  
24
@property (nonatomic, strong, readonly, nonnull) YapDatabaseConnection *readConnection;
25
@property (nonatomic, strong, readonly, nonnull) YapDatabaseConnection *writeConnection;
24
@property (nonatomic, strong, readonly, nonnull) YapDatabaseConnection *connection;
26 25

  
27 26
@end
28 27

  
......
44 43
-(instancetype)init
45 44
{
46 45
    if (self = [super init]) {
47
        _readConnection = OTRDatabaseManager.shared.readOnlyDatabaseConnection;
48
        _writeConnection = OTRDatabaseManager.shared.readWriteDatabaseConnection;
46
        _connection = OTRDatabaseManager.shared.readWriteDatabaseConnection;
49 47
    }
50 48
    return self;
51 49
}
......
66 64
}
67 65

  
68 66
/** When created, it is still unsaved and must be manually saved within a yap transaction. */
69
- (nullable OTRXMPPBuddy *)createBuddyWithJID:(XMPPJID *)jid stream:(XMPPStream *)stream {
67
- (nullable OTRXMPPBuddy *)fetchOrCreateBuddyWithJID:(XMPPJID *)jid stream:(XMPPStream *)stream {
68
    if ([stream.myJID isEqualToJID:jid options:XMPPJIDCompareBare]) {
69
        DDLogWarn(@"Adding self to roster: %@", jid);
70
    }
70 71
    NSString *accountUniqueId = [self accountUniqueIdForStream:stream];
71
    OTRXMPPBuddy *buddy = [[OTRXMPPBuddy alloc] init];
72
    __block OTRXMPPBuddy *buddy = nil;
73
    [self.connection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
74
        buddy = [self fetchBuddyWithJID:jid stream:stream transaction:transaction];
75
    }];
76
    if (!buddy) {
77
        buddy = [[OTRXMPPBuddy alloc] init];
78
    }
72 79
    buddy.username = [jid bare];
73 80
    buddy.accountUniqueId = accountUniqueId;
74 81
    return buddy;
75 82
}
76 83

  
77
- (nullable OTRXMPPBuddy*) createBuddyFromRosterItem:(NSXMLElement *)rosterItem stream:(XMPPStream *)stream {
84
- (nullable OTRXMPPBuddy*) fetchOrCreateBuddyFromRosterItem:(NSXMLElement *)rosterItem stream:(XMPPStream *)stream {
78 85
    NSString *jidStr = [rosterItem attributeStringValueForName:@"jid"];
79 86
    XMPPJID *jid = [[XMPPJID jidWithString:jidStr] bareJID];
80
    return [self createBuddyWithJID:jid stream:stream];
87
    return [self fetchOrCreateBuddyWithJID:jid stream:stream];
81 88
}
82 89

  
83 90

  
......
103 110
- (BOOL)existsBuddyWithJID:(XMPPJID *)jid xmppStram:(XMPPStream *)stream
104 111
{
105 112
    __block BOOL result = NO;
106
    [self.readConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
113
    [self.connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
107 114
        OTRBuddy *buddy = [self fetchBuddyWithJID:jid stream:stream transaction:transaction];
108 115
        if (buddy) {
109 116
            result = YES;
......
149 156
    if (!item) { return; }
150 157
    BOOL newlyCreatedBuddy = NO;
151 158
    if (!buddy) {
152
        buddy = [self createBuddyFromRosterItem:item stream:stream];
159
        buddy = [self fetchOrCreateBuddyFromRosterItem:item stream:stream];
153 160
        if (!buddy) {
154 161
            return;
155 162
        }
......
177 184
        return;
178 185
    }
179 186
    
180
    [self.writeConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
187
    [self.connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
181 188
        [newBuddy saveWithTransaction:transaction];
182 189
    }];
183 190
    
......
216 223
    }
217 224
    
218 225
    __block OTRXMPPBuddy *buddy = nil;
219
    [self.readConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
226
    [self.connection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
220 227
        buddy = [self fetchBuddyWithJID:jid stream:stream transaction:transaction];
221 228
    }];
222 229
    NSString *subscription = [item attributeStringValueForName:@"subscription"];
223 230
    if ([subscription isEqualToString:@"remove"])
224 231
    {
225 232
        if (buddy) {
226
            [self.writeConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
233
            [self.connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
227 234
                [transaction removeObjectForKey:buddy.uniqueId inCollection:[OTRXMPPBuddy collection]];
228 235
            }];
229 236
        }
......
235 242
- (void)handlePresence:(XMPPPresence *)presence xmppStream:(XMPPStream *)stream
236 243
{
237 244
    __block OTRXMPPBuddy *buddy = nil;
238
    [self.readConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
245
    [self.connection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
239 246
        buddy = [self fetchBuddyWithJID:[presence from] stream:stream transaction:transaction];
240 247
    }];
241 248
    BOOL newlyCreatedBuddy = NO;
242 249
    if (!buddy) {
243
        buddy = [self createBuddyWithJID:[presence from] stream:stream];
250
        buddy = [self fetchOrCreateBuddyWithJID:[presence from] stream:stream];
244 251
        if (!buddy) {
245 252
            return;
246 253
        }
......
289 296
    // If this contact is pending approval but you see their presence
290 297
    // then they're not pending anymore
291 298
    if (newStatus != OTRThreadStatusOffline && newBuddy.pendingApproval) {
292
        [self.writeConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
299
        [self.connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
293 300
            OTRXMPPBuddy *buddy = [[OTRXMPPBuddy fetchObjectWithUniqueID:newBuddy.uniqueId transaction:transaction] copy];
294 301
            if (!buddy) { return; }
295 302
            buddy.pendingApproval = NO;
......
321 328
    
322 329
    // Save if it's a new buddy
323 330
    if (newlyCreatedBuddy) {
324
        [self.writeConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
331
        [self.connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
325 332
            [newBuddy saveWithTransaction:transaction];
326 333
        }];
327 334
    }
......
351 358
- (NSArray *)jidsForXMPPStream:(XMPPStream *)stream
352 359
{
353 360
    __block NSMutableArray *jidArray = [NSMutableArray array];
354
    [self.readConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
361
    [self.connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
355 362
        [transaction enumerateKeysAndObjectsInCollection:[OTRXMPPBuddy collection] usingBlock:^(NSString *key, id object, BOOL *stop) {
356 363
            if ([object isKindOfClass:[OTRXMPPBuddy class]]) {
357 364
                OTRXMPPBuddy *buddy = (OTRXMPPBuddy *)object;
ChatSecure/Classes/Controllers/XMPP/XMPPManager.swift
1
//
2
//  XMPPManager.swift
3
//  ChatSecureCore
4
//
5
//  Created by Chris Ballinger on 11/27/17.
6
//  Copyright © 2017 Chris Ballinger. All rights reserved.
7
//
8

  
9
import Foundation
10
import XMPPFramework
11

  
12
extension XMPPMessageArchiveManagement {
13
    public func fetchHistory(archiveJID: XMPPJID? = nil, userJID: XMPPJID? = nil, since: Date? = nil) {
14
        var fields: [XMLElement] = []
15
        
16
        if let userJID = userJID {
17
            let with = XMPPMessageArchiveManagement.field(withVar: "with", type: nil, andValue: userJID.bare)
18
            fields.append(with)
19
        }
20
        
21
        if let since = since {
22
            let xmppDateString = (since as NSDate).xmppDateTimeString
23
            
24
            let start = XMPPMessageArchiveManagement.field(withVar: "start", type: nil, andValue: xmppDateString)
25
            fields.append(start)
26
        }
27
        retrieveMessageArchive(at: archiveJID, withFields: fields, with: nil)
28
    }
29
}
30

  
31
/// Formerly known in Obj-C as OTRXMPPManager
32
public extension XMPPManager {
33
    
34
    
35
    /** Fetches history for a thread after the most recent message */
36
    @objc public func fetchHistoryForThread(_ thread: OTRThreadOwner, transaction: YapDatabaseReadTransaction) {
37
        var archiveJID: XMPPJID? = nil
38
        var userJID: XMPPJID? = nil
39
        if let buddy = thread as? OTRXMPPBuddy,
40
            let buddyJID = buddy.bareJID {
41
            archiveJID = account.bareJID
42
            userJID = buddyJID
43
        } else if let room = thread as? OTRXMPPRoom {
44
            archiveJID = room.roomJID
45
        }
46
        let lastMessageDate = thread.lastMessage(with: transaction)?.messageDate
47
        messageStorage.archiving.fetchHistory(archiveJID: archiveJID, userJID: userJID, since: lastMessageDate)
48
    }
49
}
ChatSecure/Classes/Model/Yap Storage/Accounts/OTRXMPPAccount+Migration.swift
13 13
    @objc public var needsMigration: Bool {
14 14
        guard let jid = bareJID else { return false }
15 15
        if OTRServerDeprecation.isDeprecated(server: jid.domain) {
16
            if !autologin, let xmpp = OTRProtocolManager.shared.protocol(for: self) as? OTRXMPPManager,
16
            if !autologin, let xmpp = OTRProtocolManager.shared.protocol(for: self) as? XMPPManager,
17 17
                xmpp.connectionStatus == .disconnected {
18 18
                return false // May have migrated
19 19
            }
ChatSecure/Classes/Model/Yap Storage/OTROutgoingMessage.h
24 24
/** Mark message as deliverd via XEP-0184.*/
25 25
@property (nonatomic, getter = isDelivered) BOOL delivered;
26 26

  
27
+ (void)receivedDeliveryReceiptForMessageId:(nonnull NSString *)messageId transaction:(nonnull YapDatabaseReadWriteTransaction*)transaction;
28

  
29 27
@end
30 28
NS_ASSUME_NONNULL_END
ChatSecure/Classes/Model/Yap Storage/OTROutgoingMessage.m
12 12

  
13 13
@implementation OTROutgoingMessage
14 14

  
15
+ (void)receivedDeliveryReceiptForMessageId:(NSString *)messageId transaction:(YapDatabaseReadWriteTransaction*)transaction
16
{
17
    __block OTROutgoingMessage *deliveredMessage = nil;
18
    [transaction enumerateMessagesWithElementId:messageId originId:messageId stanzaId:nil block:^(id<OTRMessageProtocol> _Nonnull message, BOOL * _Null_unspecified stop) {
19
        if ([message isKindOfClass:[self class]]) {
20
            deliveredMessage = (OTROutgoingMessage *)message;
21
            *stop = YES;
22
        }
23
    }];
24
    
25
    // OTRDATA Media messages are not delivered until the transfer is complete. This is handled in the OTREncryptionManager.
26
    if (deliveredMessage.mediaItemUniqueId.length > 0 &&
27
        deliveredMessage.text.length == 0) {
28
        return;
29
    }
30
    
31
    if (deliveredMessage) {
32
        deliveredMessage = [deliveredMessage copy];
33
        deliveredMessage.delivered = YES;
34
        deliveredMessage.dateDelivered = [NSDate date];
35
        [deliveredMessage saveWithTransaction:transaction];
36
    }
37
}
38

  
39 15
#pragma MARK - OTRMessageProtocol 
40 16

  
41 17
- (BOOL)isMessageIncoming
ChatSecure/Classes/Model/Yap Storage/SecondaryIndexes.swift
202 202
        if matchingBuddies.count > 1 {
203 203
            DDLogWarn("WARN: More than one OTRXMPPBuddy matching query \(query) \(jid) \(accountUniqueId): \(matchingBuddies.count)")
204 204
        }
205
        #if DEBUG
206
            if matchingBuddies.count == 0 {
207
                DDLogWarn("WARN: No OTRXMPPBuddy matching query \(jid) \(accountUniqueId)")
208
                let buddy = slowLookup(jid: jid, accountUniqueId: accountUniqueId, transaction: transaction)
209
                if buddy != nil {
210
                    DDLogWarn("WARN: Found buddy using O(n) lookup that wasn't found in secondary index: \(jid) \(accountUniqueId)")
211
                }
212
            }
213
        #endif
205
//        #if DEBUG
206
//            if matchingBuddies.count == 0 {
207
//                DDLogWarn("WARN: No OTRXMPPBuddy matching query \(jid) \(accountUniqueId)")
208
//                let buddy = slowLookup(jid: jid, accountUniqueId: accountUniqueId, transaction: transaction)
209
//                if buddy != nil {
210
//                    DDLogWarn("WARN: Found buddy using O(n) lookup that wasn't found in secondary index: \(jid) \(accountUniqueId)")
211
//                }
212
//            }
213
//        #endif
214 214
        return matchingBuddies.first
215 215
    }
216 216
}
ChatSecure/Classes/View Controllers/AccountDetailViewController.swift
46 46
    var detailCells: [DetailCellInfo] = []
47 47
    let DetailCellIdentifier = "DetailCellIdentifier"
48 48
    
49
    let xmpp: OTRXMPPManager
49
    let xmpp: XMPPManager
50 50
    
51
    @objc public init(account: OTRXMPPAccount, xmpp: OTRXMPPManager, longLivedReadConnection: YapDatabaseConnection, writeConnection: YapDatabaseConnection) {
51
    @objc public init(account: OTRXMPPAccount, xmpp: XMPPManager, longLivedReadConnection: YapDatabaseConnection, writeConnection: YapDatabaseConnection) {
52 52
        self.account = account
53 53
        self.longLivedReadConnection = longLivedReadConnection
54 54
        self.writeConnection = writeConnection
......
143 143
        let cancel = UIAlertAction(title: CANCEL_STRING(), style: .cancel)
144 144
        let delete = UIAlertAction(title: DELETE_ACCOUNT_BUTTON_STRING(), style: .destructive) { (action) in
145 145
            let protocols = OTRProtocolManager.sharedInstance()
146
            if let xmpp = protocols.protocol(for: account) as? OTRXMPPManager,
146
            if let xmpp = protocols.protocol(for: account) as? XMPPManager,
147 147
                xmpp.connectionStatus != .disconnected {
148 148
                xmpp.disconnect()
149 149
            }
......
165 165
        let cancel = UIAlertAction(title: CANCEL_STRING(), style: .cancel)
166 166
        let logout = UIAlertAction(title: LOGOUT_STRING(), style: .destructive) { (action) in
167 167
            let protocols = OTRProtocolManager.sharedInstance()
168
            if let xmpp = protocols.protocol(for: account) as? OTRXMPPManager,
168
            if let xmpp = protocols.protocol(for: account) as? XMPPManager,
169 169
                xmpp.connectionStatus != .disconnected {
170 170
                xmpp.disconnect()
171 171
            }
ChatSecure/Classes/View Controllers/OTRMessagesViewController.m
223 223
        [self scrollToBottomAnimated:animated];
224 224
    });
225 225
    self.loadingMessages = NO;
226
    [self fetchMessageHistory];
226 227
}
227 228

  
228 229
- (void)viewWillAppear:(BOOL)animated
......
311 312

  
312 313
#pragma - mark Setters & getters
313 314

  
315
- (void) fetchMessageHistory {
316
    [self.readOnlyDatabaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
317
        id<OTRThreadOwner> thread = [self threadObjectWithTransaction:transaction];
318
        OTRXMPPManager *xmpp = [self xmppManagerWithTransaction:transaction];
319
        [xmpp fetchHistoryForThread:thread transaction:transaction];
320
    }];
321
}
322

  
314 323
- (OTRAttachmentPicker *)attachmentPicker
315 324
{
316 325
    if (!_attachmentPicker) {
......
491 500
            account = [OTRAccount fetchObjectWithUniqueID:buddy.accountUniqueId transaction:transaction];
492 501
        }];
493 502
        
494
        
495
        
496 503
        //Update UI now
497 504
        if (buddy.chatState == OTRChatStateComposing || buddy.chatState == OTRChatStatePaused) {
498 505
            self.showTypingIndicator = YES;
ChatSecure/Classes/View Controllers/OTRRoomOccupantsViewController.swift
303 303
    
304 304
    /** Do not call this within a yap transaction! */
305 305
    fileprivate func xmppRoom() -> XMPPRoom? {
306
        var xmpp: OTRXMPPManager? = nil
306
        var xmpp: XMPPManager? = nil
307 307
        self.readConnection?.read { transaction in
308 308
            if let account = self.room?.account(with: transaction) {
309
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? OTRXMPPManager
309
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? XMPPManager
310 310
            }
311 311
        }
312 312
        guard let room = self.room,
......
318 318
    }
319 319
    
320 320
    fileprivate func xmppRoomManager() -> OTRXMPPRoomManager? {
321
        var xmpp: OTRXMPPManager? = nil
321
        var xmpp: XMPPManager? = nil
322 322
        self.readConnection?.read { transaction in
323 323
            if let account = self.room?.account(with: transaction) {
324
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? OTRXMPPManager
324
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? XMPPManager
325 325
            }
326 326
        }
327 327
        return xmpp?.roomManager
ChatSecure/Classes/View Controllers/UserProfileViewController.swift
35 35
            return nil
36 36
        }
37 37
        
38
        guard let xmpp = OTRProtocolManager.sharedInstance().protocol(for: acct) as? OTRXMPPManager else {
38
        guard let xmpp = OTRProtocolManager.sharedInstance().protocol(for: acct) as? XMPPManager else {
39 39
            return nil
40 40
        }
41 41
        return xmpp.omemoSignalCoordinator
......
300 300
        yourProfileRow.value = account
301 301
        yourProfileSection.addFormRow(yourProfileRow)
302 302
        
303
        guard let xmpp = OTRProtocolManager.sharedInstance().protocol(for: account) as? OTRXMPPManager else {
303
        guard let xmpp = OTRProtocolManager.sharedInstance().protocol(for: account) as? XMPPManager else {
304 304
            return form
305 305
        }
306 306
        guard let myBundle = xmpp.omemoSignalCoordinator?.fetchMyBundle() else {
ChatSecure/Classes/Views/Cells/MediaDownloadView.swift
43 43
        
44 44
        self.downloadAction = { [weak self] view, sender in
45 45
            self?.downloadButton.isEnabled = false
46
            var xmpp: OTRXMPPManager? = nil
46
            var xmpp: XMPPManager? = nil
47 47
            OTRDatabaseManager.shared.readOnlyDatabaseConnection?.read { transaction in
48 48
                guard let thread = message.threadOwner(with: transaction) else { return }
49 49
                guard let account = thread.account(with: transaction) else { return }
50
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? OTRXMPPManager
50
                xmpp = OTRProtocolManager.shared.protocol(for: account) as? XMPPManager
51 51
                xmpp?.fileTransferManager.downloadMediaIfNeeded(message)
52 52
            }
53 53
        }
Submodules/XMPPFramework
1
Subproject commit efa1f9d3b8334aa3d4636eb238ff3b363c2697ac
1
Subproject commit f5f1cea4d4553fed05fe388a747c73398ca7568a

Also available in: Unified diff