mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			124 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			124 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Swift
		
	
| //
 | |
| //  Copyright (c) 2018 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| import Foundation
 | |
| import PromiseKit
 | |
| import SignalServiceKit
 | |
| 
 | |
| @objc
 | |
| public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
 | |
| 
 | |
|     private static var sharedCompleteRegistrationFixerJob: CompleteRegistrationFixerJob?
 | |
| 
 | |
|     // increment a similar constant for each migration.
 | |
|     @objc
 | |
|     class func migrationId() -> String {
 | |
|         return "106"
 | |
|     }
 | |
| 
 | |
|     // Overriding runUp since we have some specific completion criteria which
 | |
|     // is more likely to fail since it involves network requests.
 | |
|     override public func runUp(completion:@escaping () -> Void) {
 | |
|         let job = CompleteRegistrationFixerJob(completionHandler: { (didSucceed) in
 | |
| 
 | |
|             if (didSucceed) {
 | |
|                 Logger.info("Completed. Saving.")
 | |
|                 self.save()
 | |
|             } else {
 | |
|                 Logger.error("Failed.")
 | |
|             }
 | |
| 
 | |
|             completion()
 | |
|         })
 | |
| 
 | |
|         type(of: self).sharedCompleteRegistrationFixerJob = job
 | |
| 
 | |
|         job.start()
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * A previous client bug made it possible for re-registering users to register their new account
 | |
|      * but never upload new pre-keys. The symptom is that there will be accounts with no uploaded
 | |
|      * identity key. We detect that here and fix the situation
 | |
|      */
 | |
|     private class CompleteRegistrationFixerJob: NSObject {
 | |
| 
 | |
|         // MARK: - Dependencies
 | |
| 
 | |
|         private var tsAccountManager: TSAccountManager {
 | |
|             return TSAccountManager.sharedInstance()
 | |
|         }
 | |
| 
 | |
|         // MARK: -
 | |
| 
 | |
|         // Duration between retries if update fails.
 | |
|         let kRetryInterval: TimeInterval = 5
 | |
| 
 | |
|         let completionHandler: (Bool) -> Void
 | |
| 
 | |
|         init(completionHandler: @escaping (Bool) -> Void) {
 | |
|             self.completionHandler = completionHandler
 | |
|         }
 | |
| 
 | |
|         func start() {
 | |
|             guard tsAccountManager.isRegistered() else {
 | |
|                 self.completionHandler(true)
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             self.ensureProfileComplete().done {
 | |
|                 Logger.info("complete. Canceling timer and saving.")
 | |
|                 self.completionHandler(true)
 | |
|             }.catch { error in
 | |
|                 let nserror = error as NSError
 | |
|                 if nserror.domain == TSNetworkManagerErrorDomain {
 | |
|                     // Don't retry if we had an unrecoverable error.
 | |
|                     // In particular, 401 (invalid auth) is unrecoverable.
 | |
|                     let isUnrecoverableError = nserror.code == 401
 | |
|                     if isUnrecoverableError {
 | |
|                         Logger.error("failed due to unrecoverable error: \(error). Aborting.")
 | |
|                         self.completionHandler(true)
 | |
|                         return
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 Logger.error("failed with \(error).")
 | |
|                 self.completionHandler(false)
 | |
|             }.retainUntilComplete()
 | |
|         }
 | |
| 
 | |
|         func ensureProfileComplete() -> Promise<Void> {
 | |
|             guard let localRecipientId = TSAccountManager.localNumber() else {
 | |
|                 // local app doesn't think we're registered, so nothing to worry about.
 | |
|                 return Promise.value(())
 | |
|             }
 | |
| 
 | |
|             return firstly {
 | |
|                 ProfileFetcherJob().getProfile(recipientId: localRecipientId)
 | |
|             }.done { _ in
 | |
|                 Logger.info("verified recipient profile is in good shape: \(localRecipientId)")
 | |
|             }.recover { error -> Promise<Void> in
 | |
|                 switch error {
 | |
|                 case SignalServiceProfile.ValidationError.invalidIdentityKey(let description):
 | |
|                     Logger.warn("detected incomplete profile for \(localRecipientId) error: \(description)")
 | |
| 
 | |
|                     let (promise, resolver) = Promise<Void>.pending()
 | |
|                     // This is the error condition we're looking for. Update prekeys to properly set the identity key, completing registration.
 | |
|                     TSPreKeyManager.createPreKeys(success: {
 | |
|                         Logger.info("successfully uploaded pre-keys. Profile should be fixed.")
 | |
|                         resolver.fulfill(())
 | |
|                     },
 | |
|                                                   failure: { _ in
 | |
|                                                     resolver.reject(OWSErrorWithCodeDescription(.signalServiceFailure, "\(self.logTag) Unknown error"))
 | |
|                     })
 | |
| 
 | |
|                     return promise
 | |
|                 default:
 | |
|                     throw error
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |