RestoreSafeViewController.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2018-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import UIKit
  21. import CocoaLumberjackSwift
  22. @objc protocol RestoreSafeViewControllerDelegate {
  23. func restoreSafeDone();
  24. func restoreSafeCancelled();
  25. }
  26. class RestoreSafeViewController: IDCreationPageViewController, UITextFieldDelegate {
  27. @IBOutlet weak var titleLabel: UILabel!
  28. @IBOutlet weak var descriptionLabel: UILabel!
  29. @IBOutlet weak var forgotIdButton: UIButton!
  30. @IBOutlet weak var identityField: SetupTextField!
  31. @IBOutlet weak var expertOptionsButton: UIButton!
  32. @IBOutlet weak var cancelButton: UIButton!
  33. @IBOutlet weak var okButton: SetupButton!
  34. @objc weak var delegate: RestoreSafeViewControllerDelegate?
  35. //restore just ID from Threems Safe, in case there is any data on this device
  36. @objc var restoreIdentityOnly: Bool = false
  37. var restoreIdentity: String?
  38. var restorePassword: String?
  39. var restoreCustomServer: String?
  40. var restoreServer: String?
  41. var restoreServerUsername: String?
  42. var restoreServerPassword: String?
  43. private var activateSafeAnyway: Bool = false
  44. override func viewDidLoad() {
  45. super.viewDidLoad()
  46. self.hideKeyboardWhenTappedAround()
  47. self.titleLabel.text = NSLocalizedString("safe_restore", comment: "")
  48. self.descriptionLabel.text = NSLocalizedString("safe_restore_enter_id", comment: "")
  49. self.forgotIdButton.setTitle(NSLocalizedString("safe_forgot_your_id", comment: ""), for: .normal)
  50. self.identityField.delegate = self
  51. self.identityField.placeholder = NSLocalizedString("safe_threema_id", comment: "")
  52. self.expertOptionsButton.setTitle(NSLocalizedString("safe_advanced_options", comment: ""), for: .normal)
  53. self.cancelButton.setTitle(NSLocalizedString("cancel", comment: ""), for: .normal)
  54. self.okButton.setTitle(NSLocalizedString("restore", comment: ""), for: .normal)
  55. self.forgotIdButton.setTitleColor(Colors.mainThemeDark(), for: .normal)
  56. //check MDM for Threema Safe restore
  57. let mdmSetup = MDMSetup(setup: true)!
  58. if mdmSetup.isManaged() {
  59. if !mdmSetup.isSafeBackupDisable() {
  60. self.activateSafeAnyway = true
  61. }
  62. if mdmSetup.isSafeRestoreServerPreset() {
  63. self.expertOptionsButton.isHidden = true
  64. let safeConfigManager = SafeConfigManager()
  65. let safeStore = SafeStore(safeConfigManager: safeConfigManager, serverApiConnector: ServerAPIConnector())
  66. self.restoreCustomServer = mdmSetup.safeServerUrl()
  67. self.restoreServer = safeStore.composeSafeServerAuth(server: mdmSetup.safeServerUrl(), user: mdmSetup.safeServerUsername(), password: mdmSetup.safeServerPassword())?.absoluteString
  68. }
  69. if mdmSetup.isSafeRestoreForce() {
  70. self.restoreIdentity = mdmSetup.safeRestoreId()
  71. self.descriptionLabel.isHidden = true
  72. self.forgotIdButton.isHidden = true
  73. self.expertOptionsButton.isHidden = true
  74. self.cancelButton.isHidden = true
  75. self.okButton.removeConstraint(self.okButton.constraints[0])
  76. self.okButton.addConstraint(NSLayoutConstraint(item: self.okButton!, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1.0, constant: 280.0))
  77. self.identityField.isUserInteractionEnabled = false
  78. self.identityField.alpha = 0.5
  79. self.identityField.text = self.restoreIdentity
  80. if mdmSetup.isSafeRestorePasswordPreset() {
  81. self.restorePassword = mdmSetup.safePassword()
  82. self.okButton.isHidden = true
  83. self.startRestore()
  84. }
  85. } else {
  86. //add swipe right for cancel action
  87. let gestureRecognizer = UISwipeGestureRecognizer(target: self, action:#selector(swipeAction))
  88. gestureRecognizer.numberOfTouchesRequired = 1
  89. self.view.addGestureRecognizer(gestureRecognizer)
  90. }
  91. } else {
  92. //add swipe right for cancel action
  93. let gestureRecognizer = UISwipeGestureRecognizer(target: self, action:#selector(swipeAction))
  94. gestureRecognizer.numberOfTouchesRequired = 1
  95. self.view.addGestureRecognizer(gestureRecognizer)
  96. }
  97. }
  98. //MARK: - Navigation
  99. override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
  100. if identifier == "RestoreSafePassword" {
  101. guard let identity = self.identityField.text, identity.count == 8 else {
  102. let alert = IntroQuestionViewHelper(parent: self, onAnswer: nil)
  103. alert.showAlert(BundleUtil.localizedString(forKey: "invalid_threema_id"))
  104. return false
  105. }
  106. }
  107. return true
  108. }
  109. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  110. self.mainContentView.isHidden = true
  111. if let safeServerViewController = segue.destination as? SafeServerViewController {
  112. safeServerViewController.isServerForRestore = true
  113. safeServerViewController.customServer = self.restoreCustomServer
  114. safeServerViewController.server = self.restoreServer
  115. safeServerViewController.serverUsername = self.restoreServerUsername
  116. safeServerViewController.serverPassword = self.restoreServerPassword
  117. }
  118. }
  119. }
  120. extension RestoreSafeViewController: SetupTextFieldDelegate {
  121. func editingChangedTextField(_ sender: SetupTextField, forEvent event: UIEvent) { }
  122. func primaryActionTriggered(_ sender: SetupTextField, forEvent event: UIEvent) {
  123. self.performSegue(withIdentifier: "RestoreSafePassword", sender: sender)
  124. }
  125. }
  126. extension RestoreSafeViewController {
  127. @IBAction func touchDownButton(_ sender: UIButton, forEvent event: UIEvent) {
  128. if sender == cancelButton {
  129. self.delegate?.restoreSafeCancelled()
  130. }
  131. }
  132. @objc func swipeAction(sender: UITapGestureRecognizer) {
  133. if sender.state == .ended {
  134. self.delegate?.restoreSafeCancelled()
  135. }
  136. }
  137. @IBAction func cancelSafeForgotId(_ segue: UIStoryboardSegue) {
  138. self.mainContentView.isHidden = false
  139. }
  140. @IBAction func okSafeForgotId(_ segue: UIStoryboardSegue) {
  141. guard let restoreSafeForgotIdViewController = segue.source as? RestoreSafeForgotIdViewController else {
  142. return
  143. }
  144. self.identityField.text = restoreSafeForgotIdViewController.selectedIdentity
  145. self.mainContentView.isHidden = false
  146. }
  147. @IBAction func cancelRestoreSafeServer(_ segue: UIStoryboardSegue) {
  148. self.mainContentView.isHidden = false
  149. }
  150. @IBAction func okRestoreSafeServer(_ segue: UIStoryboardSegue) {
  151. guard let safeServerViewController = segue.source as? SafeServerViewController else {
  152. return
  153. }
  154. self.mainContentView.isHidden = false
  155. self.restoreCustomServer = safeServerViewController.customServer
  156. self.restoreServer = safeServerViewController.server
  157. self.restoreServerUsername = safeServerViewController.serverUsername
  158. self.restoreServerPassword = safeServerViewController.serverPassword
  159. }
  160. @IBAction func cancelSafePassword(_ segue: UIStoryboardSegue) {
  161. self.mainContentView.isHidden = false
  162. }
  163. @IBAction func okSafePassword(_ segue: UIStoryboardSegue) {
  164. self.mainContentView.isHidden = false
  165. if let restoreSafePasswordViewController = segue.source as? RestoreSafePasswordViewController,
  166. let password = restoreSafePasswordViewController.passwordField.text,
  167. let identity = self.identityField.text {
  168. self.restoreIdentity = identity
  169. self.restorePassword = password
  170. self.startRestore()
  171. }
  172. }
  173. func startRestore() {
  174. // start restore in timer to give activity indicator time to start
  175. self.view.isUserInteractionEnabled = false
  176. MBProgressHUD.showAdded(to: self.view, animated: true)
  177. Timer.scheduledTimer(timeInterval: TimeInterval(0.3), target: self, selector: #selector(self.restore), userInfo: nil, repeats: false)
  178. }
  179. @objc func restore() {
  180. let logFile = FileUtility.appDocumentsDirectory?.appendingPathComponent("safe-restore.log")
  181. LogManager.deleteLogFile(logFile)
  182. LogManager.addFileLogger(logFile)
  183. DDLogNotice("Threema Safe restore started")
  184. let safeConfigManager = SafeConfigManager()
  185. let safeStore = SafeStore(safeConfigManager: safeConfigManager, serverApiConnector: ServerAPIConnector())
  186. let safeManager = SafeManager(safeConfigManager: safeConfigManager, safeStore: safeStore, safeApiService: SafeApiService())
  187. safeManager.startRestore(identity: self.restoreIdentity!, password: self.restorePassword!, customServer: self.restoreCustomServer, server: self.restoreServer, restoreIdentityOnly: self.restoreIdentityOnly, activateSafeAnyway: self.activateSafeAnyway, completionHandler: { (error) in
  188. DispatchQueue.main.async {
  189. MBProgressHUD.hide(for: self.view, animated: true)
  190. self.view.isUserInteractionEnabled = true
  191. if let error = error {
  192. switch error {
  193. case .restoreError(let message):
  194. DDLogError(message)
  195. let alert = IntroQuestionViewHelper(parent: self, onAnswer: { (sender, answer) in
  196. self.delegate?.restoreSafeDone()
  197. })
  198. alert.showAlert(message, title: BundleUtil.localizedString(forKey: "safe_restore_failed"))
  199. case .restoreFailed(let message):
  200. DDLogError(message)
  201. let alert = IntroQuestionViewHelper(parent: self, onAnswer: nil)
  202. alert.showAlert(message, title: BundleUtil.localizedString(forKey: "safe_restore_failed"))
  203. default: break
  204. }
  205. } else {
  206. DDLogNotice("Threema Safe restore successfully finished")
  207. self.delegate?.restoreSafeDone()
  208. }
  209. LogManager.removeFileLogger(logFile)
  210. }
  211. })
  212. }
  213. }