// // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // import Foundation import PromiseKit import SignalServiceKit /** * Signal is actually two services - textSecure for messages and red phone (for calls). * AccountManager delegates to both. */ @objc public class AccountManager: NSObject { let textSecureAccountManager: TSAccountManager var pushManager: PushManager { // dependency injection hack since PushManager has *alot* of dependencies, and would induce a cycle. return PushManager.shared() } @objc public required init(textSecureAccountManager: TSAccountManager) { self.textSecureAccountManager = textSecureAccountManager super.init() SwiftSingletons.register(self) } // MARK: - Singletons private var networkManager: TSNetworkManager { return SSKEnvironment.shared.networkManager } private var preferences: OWSPreferences { return Environment.shared.preferences } // MARK: registration @objc func register(verificationCode: String, pin: String?) -> AnyPromise { return AnyPromise(register(verificationCode: verificationCode, pin: pin)) } func register(verificationCode: String, pin: String?) -> Promise { guard verificationCode.count > 0 else { let error = OWSErrorWithCodeDescription(.userError, NSLocalizedString("REGISTRATION_ERROR_BLANK_VERIFICATION_CODE", comment: "alert body during registration")) return Promise(error: error) } Logger.debug("registering with signal server") let registrationPromise: Promise = firstly { return self.registerForTextSecure(verificationCode: verificationCode, pin: pin) }.then { return self.syncPushTokens() }.recover { (error) -> Promise in switch error { case PushRegistrationError.pushNotSupported(let description): // This can happen with: // - simulators, none of which support receiving push notifications // - on iOS11 devices which have disabled "Allow Notifications" and disabled "Enable Background Refresh" in the system settings. Logger.info("Recovered push registration error. Registering for manual message fetcher because push not supported: \(description)") return self.registerForManualMessageFetching() default: throw error } }.then { self.completeRegistration() } registrationPromise.retainUntilComplete() return registrationPromise } private func registerForTextSecure(verificationCode: String, pin: String?) -> Promise { return Promise { fulfill, reject in self.textSecureAccountManager.verifyAccount(withCode: verificationCode, pin: pin, success: fulfill, failure: reject) } } private func syncPushTokens() -> Promise { Logger.info("") let job = SyncPushTokensJob(accountManager: self, preferences: self.preferences) job.uploadOnlyIfStale = false return job.run() } private func completeRegistration() { Logger.info("") self.textSecureAccountManager.didRegister() } // MARK: Message Delivery func updatePushTokens(pushToken: String, voipToken: String) -> Promise { return Promise { fulfill, reject in self.textSecureAccountManager.registerForPushNotifications(pushToken: pushToken, voipToken: voipToken, success: fulfill, failure: reject) } } func registerForManualMessageFetching() -> Promise { TSAccountManager.sharedInstance().setIsManualMessageFetchEnabled(true) // Try to update the account attributes to reflect this change. let request = OWSRequestFactory.updateAttributesRequest() let promise: Promise = self.networkManager.makePromise(request: request) .then(execute: { (_, _) in Logger.info("updated server with account attributes to enableManualFetching") }).catch(execute: { (error) in Logger.error("failed to update server with account attributes with error: \(error)") }) return promise } // MARK: Turn Server func getTurnServerInfo() -> Promise { return Promise { fulfill, reject in self.networkManager.makeRequest(OWSRequestFactory.turnServerInfoRequest(), success: { (_: URLSessionDataTask, responseObject: Any?) in guard responseObject != nil else { return reject(OWSErrorMakeUnableToProcessServerResponseError()) } if let responseDictionary = responseObject as? [String: AnyObject] { if let turnServerInfo = TurnServerInfo(attributes: responseDictionary) { return fulfill(turnServerInfo) } Logger.error("unexpected server response:\(responseDictionary)") } return reject(OWSErrorMakeUnableToProcessServerResponseError()) }, failure: { (_: URLSessionDataTask, error: Error) in return reject(error) }) } } }