diff --git a/Podfile b/Podfile index b0cad6357..b18fb76f4 100644 --- a/Podfile +++ b/Podfile @@ -4,7 +4,7 @@ source 'https://github.com/CocoaPods/Specs.git' target 'Signal' do pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git' - pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/check-for-keychain-errors' + pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/censorship-circumvention' #pod 'SignalServiceKit', path: '../SignalServiceKit' pod 'OpenSSL' pod 'PastelogKit', '~> 1.3' diff --git a/Podfile.lock b/Podfile.lock index b6e4c460f..c6594101c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -43,7 +43,7 @@ PODS: - Reachability (3.2) - SAMKeychain (1.5.2) - SCWaveformView (1.0.0) - - SignalServiceKit (0.8.1): + - SignalServiceKit (0.9.0): - '25519' - AFNetworking - AxolotlKit @@ -121,7 +121,7 @@ DEPENDENCIES: - OpenSSL - PastelogKit (~> 1.3) - SCWaveformView (~> 1.0) - - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/check-for-keychain-errors`) + - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/censorship-circumvention`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) - ZXingObjC @@ -129,7 +129,7 @@ EXTERNAL SOURCES: AxolotlKit: :git: https://github.com/WhisperSystems/SignalProtocolKit.git SignalServiceKit: - :branch: mkirk/check-for-keychain-errors + :branch: mkirk/censorship-circumvention :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :git: https://github.com/facebook/SocketRocket.git @@ -139,7 +139,7 @@ CHECKOUT OPTIONS: :commit: 714f5ebe199ecc999b33c6f97a4bb57e2db90e75 :git: https://github.com/WhisperSystems/SignalProtocolKit.git SignalServiceKit: - :commit: 5ccbd4ca6dcb97032af3641b6689866e2265510f + :commit: 78515377b14f1055c4a87548a7899650b753ce52 :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 41b57bb2fc292a814f758441a05243eb38457027 @@ -162,7 +162,7 @@ SPEC CHECKSUMS: Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 1865333198217411f35327e8da61b43de79b635b SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07 - SignalServiceKit: 60f92ec89fbbf3196bd786b88f065c0214db9ca8 + SignalServiceKit: 59a79a51b89b963ba94db30cc99ed5212da0bb9f SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c @@ -170,6 +170,6 @@ SPEC CHECKSUMS: YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f ZXingObjC: bf15b3814f7a105b6d99f47da2333c93a063650a -PODFILE CHECKSUM: 52f34af46d045b2c4c012e12a04d06b69f0dae56 +PODFILE CHECKSUM: e09325f010ba0ef1fd0bfa07f665e7be73c43ee0 COCOAPODS: 1.0.1 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0b4180aeb..bacb906cd 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ 452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */; }; 452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; }; 452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; }; + 452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; }; + 452ECA4E1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; }; 4531C9C41DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */; }; 453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; }; 453D28BA1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; }; @@ -566,6 +568,7 @@ 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MesssagesBubblesSizeCalculatorTest.swift; path = Models/MesssagesBubblesSizeCalculatorTest.swift; sourceTree = ""; }; 452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsTableViewController.h; sourceTree = ""; }; 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSConversationSettingsTableViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageFetcherJob.swift; path = Jobs/MessageFetcherJob.swift; sourceTree = ""; }; 4531C9C21DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMessagesCollectionViewCell+OWS.h"; sourceTree = ""; }; 4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMessagesCollectionViewCell+OWS.m"; sourceTree = ""; }; 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = ""; }; @@ -1295,6 +1298,7 @@ children = ( 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */, 45D231761DC7E8F10034FA89 /* SessionResetJob.swift */, + 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */, ); name = Jobs; sourceTree = ""; @@ -2923,6 +2927,7 @@ 45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */, E197B61E18BBEC6D00F073E5 /* AudioRouter.m in Sources */, E197B60D18BBEC1A00F073E5 /* AudioSocket.m in Sources */, + 452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */, A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */, 45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */, FC31962D1A06A2190094C78E /* FingerprintViewController.m in Sources */, @@ -3062,6 +3067,7 @@ B660F7091C29988E00687D6E /* DesiredBufferDepthController.m in Sources */, B660F70A1C29988E00687D6E /* DropoutTracker.m in Sources */, B660F70B1C29988E00687D6E /* JitterQueue.m in Sources */, + 452ECA4E1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */, B660F70C1C29988E00687D6E /* StretchFactorController.m in Sources */, B660F70D1C29988E00687D6E /* AnonymousAudioCallbackHandler.m in Sources */, B660F70E1C29988E00687D6E /* RemoteIOAudio.m in Sources */, diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 8d22f826f..adbf95e70 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -38,7 +38,7 @@ CFBundleVersion - 2.6.9.1 + 2.6.9.4 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/Signal/src/Jobs/MessageFetcherJob.swift b/Signal/src/Jobs/MessageFetcherJob.swift new file mode 100644 index 000000000..0a7514c41 --- /dev/null +++ b/Signal/src/Jobs/MessageFetcherJob.swift @@ -0,0 +1,197 @@ +// Created by Michael Kirk on 12/19/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +import Foundation +import PromiseKit + +@objc(OWSMessageFetcherJob) +class MessageFetcherJob: NSObject { + + let TAG = "[MessageFetcherJob]" + var timer: Timer? + + // MARK: injected dependencies + let networkManager: TSNetworkManager + let messagesManager: TSMessagesManager + let messageSender: MessageSender + let signalService: OWSSignalService + +// var fallbackTransport = false + // ENABLED FOR DEBUG. DO NOT COMMIT! + var fallbackTransport = true + var runPromises = [Double: Promise]() + + init(messagesManager: TSMessagesManager, messageSender: MessageSender, networkManager: TSNetworkManager, signalService: OWSSignalService) { + self.messagesManager = messagesManager + self.networkManager = networkManager + self.messageSender = messageSender + self.signalService = signalService + } + + func runAsync() { + Logger.debug("\(TAG) \(#function)") + guard signalService.isCensored else { + Logger.debug("\(self.TAG) delegating message fetching to SocketManager since we're using normal transport.") + TSSocketManager.becomeActive(fromBackgroundExpectMessage: true) + return + } + + Logger.info("\(TAG) using fallback message fetching.") + + let promiseId = NSDate().timeIntervalSince1970 + Logger.debug("\(self.TAG) starting promise: \(promiseId)") + let runPromise = self.fetchUndeliveredMessages().then { (envelopes: [OWSSignalServiceProtosEnvelope], more: Bool) -> () in + for envelope in envelopes { + Logger.info("\(self.TAG) received envelope.") + self.messagesManager.handleReceivedEnvelope(envelope); + + self.acknowledgeDelivery(envelope: envelope) + } + if more { + Logger.info("\(self.TAG) more messages, so recursing.") + // recurse + self.runAsync() + } + }.always { + Logger.debug("\(self.TAG) cleaning up promise: \(promiseId)") + self.runPromises[promiseId] = nil + } + + // maintain reference to make sure it's not de-alloced prematurely. + runPromises[promiseId] = runPromise + } + + // use in DEBUG or wherever you can't receive push notifications to poll for messages. + // Do not use in production. + func startRunLoop(timeInterval: Double) { + Logger.error("\(TAG) Starting message fetch polling. This should not be used in production."); + timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(runAsync), userInfo: nil, repeats: true) + } + + func stopRunLoop() { + timer?.invalidate() + timer = nil + } + + func parseMessagesResponse(responseObject: Any?) -> (envelopes: [OWSSignalServiceProtosEnvelope], more: Bool)? { + guard let responseObject = responseObject else { + Logger.error("\(self.TAG) response object was surpringly nil") + return nil + } + + guard let responseDict = responseObject as? [String: Any] else { + Logger.error("\(self.TAG) response object was not a dictionary") + return nil + } + + guard let messageDicts = responseDict["messages"] as? [[String: Any]] else { + Logger.error("\(self.TAG) messages object was not a list of dictionaries") + return nil + } + + let moreMessages = { () -> Bool in + if let responseMore = responseDict["more"] as? Bool { + return responseMore + } else { + Logger.warn("\(self.TAG) more object was not a bool. Assuming no more") + return false + } + }() + + let envelopes = messageDicts.map { buildEnvelope(messageDict: $0) }.filter { $0 != nil }.map { $0! } + + return ( + envelopes: envelopes, + more: moreMessages + ) + } + + func buildEnvelope(messageDict: [String: Any]) -> OWSSignalServiceProtosEnvelope? { + let builder = OWSSignalServiceProtosEnvelopeBuilder() + + guard let typeInt = messageDict["type"] as? Int32 else { + Logger.error("\(TAG) message body didn't have type") + return nil + } + + guard let type = OWSSignalServiceProtosEnvelopeType(rawValue:typeInt) else { + Logger.error("\(TAG) message body type was invalid") + return nil + } + builder.setType(type) + + if let relay = messageDict["relay"] as? String { + builder.setRelay(relay) + } + + guard let timestamp = messageDict["timestamp"] as? UInt64 else { + Logger.error("\(TAG) message body didn't have timestamp") + return nil + } + builder.setTimestamp(timestamp) + + guard let source = messageDict["source"] as? String else { + Logger.error("\(TAG) message body didn't have source") + return nil + } + builder.setSource(source) + + guard let sourceDevice = messageDict["sourceDevice"] as? UInt32 else { + Logger.error("\(TAG) message body didn't have sourceDevice") + return nil + } + builder.setSourceDevice(sourceDevice) + + if let encodedLegacyMessage = messageDict["message"] as? String { + Logger.debug("\(TAG) message body had legacyMessage") + if let legacyMessage = Data(base64Encoded: encodedLegacyMessage) { + builder.setLegacyMessage(legacyMessage) + } + } + + if let encodedContent = messageDict["content"] as? String { + Logger.debug("\(TAG) message body had content") + if let content = Data(base64Encoded: encodedContent) { + builder.setContent(content) + } + } + + return builder.build() + } + + func fetchUndeliveredMessages() -> Promise<(envelopes: [OWSSignalServiceProtosEnvelope], more: Bool)> { + return Promise { fulfill, reject in + let messagesRequest = OWSGetMessagesRequest() + + self.networkManager.makeRequest( + messagesRequest, + success: { (task: URLSessionDataTask?, responseObject: Any?) -> () in + guard let (envelopes, more) = self.parseMessagesResponse(responseObject: responseObject) else { + Logger.error("\(self.TAG) response object had unexpected content") + return reject(OWSErrorMakeUnableToProcessServerResponseError()) + } + + fulfill((envelopes: envelopes, more: more)) + }, + failure: { (task: URLSessionDataTask?, error: Error?) in + guard let error = error else { + Logger.error("\(self.TAG) error was surpringly nil. sheesh rough day.") + return reject(OWSErrorMakeUnableToProcessServerResponseError()) + } + + reject(error) + }) + } + } + + func acknowledgeDelivery(envelope: OWSSignalServiceProtosEnvelope) { + let request = OWSAcknowledgeMessageDeliveryRequest(source: envelope.source, timestamp: envelope.timestamp) + self.networkManager.makeRequest(request, + success: { (task: URLSessionDataTask?, responseObject: Any?) -> () in + Logger.debug("\(self.TAG) acknowledged delivery for message at timestamp: \(envelope.timestamp)") + }, + failure: { (task: URLSessionDataTask?, error: Error?) in + Logger.debug("\(self.TAG) acknowledging delivery for message at timestamp: \(envelope.timestamp) failed with error: \(error)") + }) + } +} diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index f588a1505..293ff4969 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -3,7 +3,9 @@ // #import +#import "Cryptography.h" #import "Environment.h" +#import "NSData+Base64.h" #import "OWSContactAvatarBuilder.h" #import "OWSContactsManager.h" #import "OWSLogger.h" @@ -11,17 +13,23 @@ #import "PropertyListPreferences.h" #import "PushManager.h" #import "RPAccountManager.h" +#import "TSSocketManager.h" #import "UIFont+OWS.h" #import "UIUtil.h" #import #import +#import #import #import +#import #import +#import #import #import #import #import +#import +#import #import #import #import diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index f21b1d589..de3b45e4e 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -15,9 +15,13 @@ #import "OWSContactsManager.h" #import "PropertyListPreferences.h" #import "RPServerRequestsManager.h" +#import "Signal-Swift.h" +#import "TSMessagesManager.h" +#import "TSAccountManager.h" #import "TSOutgoingMessage.h" #import "TSSocketManager.h" #import +#import #define pushManagerDomain @"org.whispersystems.pushmanager" @@ -31,6 +35,7 @@ @property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask; @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; +@property (nonatomic, readonly) OWSMessageFetcherJob *messageFetcherJob; @end @@ -72,6 +77,18 @@ contactsManager:contactsManager contactsUpdater:contactsUpdater]; + TSMessagesManager *messagesManager = [[TSMessagesManager alloc] initWithNetworkManager:networkManager + storageManager:storageManager + contactsManager:contactsManager + contactsUpdater:contactsUpdater + messageSender:_messageSender]; + + OWSSignalService *signalService = [OWSSignalService new]; + _messageFetcherJob = [[OWSMessageFetcherJob alloc] initWithMessagesManager:messagesManager + messageSender:_messageSender + networkManager:networkManager + signalService:signalService]; + _missingPermissionsAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"") delegate:nil @@ -86,6 +103,8 @@ #pragma mark Manage Incoming Push - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + if ([self isRedPhonePush:userInfo]) { ResponderSessionDescriptor *call; if (![self.notificationTracker shouldProcessNotification:userInfo]) { @@ -139,9 +158,7 @@ } } } else { - if (![self applicationIsActive]) { - [TSSocketManager becomeActiveFromBackgroundExpectMessage:YES]; - } + [self.messageFetcherJob runAsync]; } } @@ -164,6 +181,8 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + if ([self isRedPhonePush:userInfo]) { [self application:application didReceiveRemoteNotification:userInfo]; } @@ -174,6 +193,8 @@ } - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key]; if (threadId && [TSThread fetchObjectWithUniqueID:threadId]) { [Environment messageThreadId:threadId]; @@ -184,6 +205,8 @@ handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler { + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + [self application:application handleActionWithIdentifier:identifier forLocalNotification:notification @@ -197,6 +220,8 @@ withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + if ([identifier isEqualToString:Signal_Message_Reply_Identifier]) { NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key]; @@ -285,6 +310,9 @@ - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type { + + DDLogInfo(@"received: %s", __PRETTY_FUNCTION__); + [self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload]; }