Revision 8d76e2e3
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