AccountSetupController.swift 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. import SafariServices
  2. import UIKit
  3. import DcCore
  4. class AccountSetupController: UITableViewController, ProgressAlertHandler {
  5. weak var coordinator: AccountSetupCoordinator?
  6. private let dcContext: DcContext
  7. private var skipOauth = false
  8. private var backupProgressObserver: Any?
  9. var configureProgressObserver: Any?
  10. var onProgressSuccess: VoidFunction? // not needed here
  11. private var oauth2Observer: Any?
  12. private let tagEmailCell = 0
  13. private let tagPasswordCell = 1
  14. private let tagAdvancedCell = 2
  15. private let tagImapServerCell = 3
  16. private let tagImapUserCell = 4
  17. private let tagImapPortCell = 5
  18. private let tagImapSecurityCell = 6
  19. private let tagSmtpServerCell = 7
  20. private let tagSmtpUserCell = 8
  21. private let tagSmtpPortCell = 9
  22. private let tagSmtpPasswordCell = 10
  23. private let tagSmtpSecurityCell = 11
  24. private let tagCertCheckCell = 12
  25. private let tagEmptyServerCell = 13
  26. private let tagDeleteAccountCell = 14
  27. private let tagRestoreCell = 15
  28. private let tagTextFieldEmail = 100
  29. private let tagTextFieldPassword = 200
  30. private let tagTextFieldImapServer = 300
  31. private let tagTextFieldImapLogin = 400
  32. private let tagTextFieldSmtpServer = 500
  33. private let tagTextFieldSmtpUser = 600
  34. private let tagTextFieldSmtpPassword = 700
  35. // add cells to sections
  36. let basicSection = 100
  37. let advancedSection = 200
  38. let restoreSection = 300
  39. let folderSection = 400
  40. let dangerSection = 500
  41. private var sections = [Int]()
  42. private lazy var basicSectionCells: [UITableViewCell] = [emailCell, passwordCell]
  43. private lazy var restoreCells: [UITableViewCell] = [restoreCell]
  44. private lazy var advancedSectionCells: [UITableViewCell] = [
  45. advancedShowCell,
  46. imapSecurityCell,
  47. imapUserCell,
  48. imapServerCell,
  49. imapPortCell,
  50. smtpSecurityCell,
  51. smtpUserCell,
  52. smtpPasswordCell,
  53. smtpServerCell,
  54. smtpPortCell,
  55. certCheckCell
  56. ]
  57. private lazy var folderCells: [UITableViewCell] = [inboxWatchCell, sentboxWatchCell, mvboxWatchCell, sendCopyToSelfCell, mvboxMoveCell]
  58. private lazy var dangerCells: [UITableViewCell] = [emptyServerCell, deleteAccountCell]
  59. private let editView: Bool
  60. private var advancedSectionShowing: Bool = false
  61. private var providerInfoShowing: Bool = false
  62. private var provider: DcProvider?
  63. // MARK: - the progress dialog
  64. lazy var progressAlert: UIAlertController = {
  65. let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
  66. alert.addAction(UIAlertAction(
  67. title: String.localized("cancel"),
  68. style: .cancel,
  69. handler: { _ in
  70. self.dcContext.stopOngoingProcess()
  71. }))
  72. return alert
  73. }()
  74. // MARK: - cells
  75. private lazy var emailCell: TextFieldCell = {
  76. let cell = TextFieldCell.makeEmailCell(delegate: self)
  77. cell.tag = tagEmailCell
  78. cell.textField.addTarget(self, action: #selector(emailCellEdited), for: .editingChanged)
  79. cell.textField.tag = tagTextFieldEmail // will be used to eventually show oAuth-Dialogue when pressing return key
  80. cell.textField.delegate = self
  81. cell.textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
  82. return cell
  83. }()
  84. private lazy var passwordCell: TextFieldCell = {
  85. let cell = TextFieldCell.makePasswordCell(delegate: self)
  86. cell.tag = tagPasswordCell
  87. cell.textField.tag = tagTextFieldPassword // will be used to eventually show oAuth-Dialogue when selecting
  88. cell.textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
  89. cell.textField.delegate = self
  90. return cell
  91. }()
  92. private lazy var providerInfoCell: ProviderInfoCell = {
  93. let cell = ProviderInfoCell()
  94. cell.onInfoButtonPressed = {
  95. [unowned self] in
  96. self.handleProviderInfoButton()
  97. }
  98. return cell
  99. }()
  100. private lazy var restoreCell: UITableViewCell = {
  101. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  102. cell.textLabel?.text = String.localized("import_backup_title")
  103. cell.accessoryType = .disclosureIndicator
  104. cell.tag = tagRestoreCell
  105. return cell
  106. }()
  107. private lazy var emptyServerCell: ActionCell = {
  108. let cell = ActionCell(frame: .zero)
  109. cell.actionTitle = String.localized("pref_empty_server_title")
  110. cell.actionColor = UIColor.red
  111. cell.tag = tagEmptyServerCell
  112. return cell
  113. }()
  114. private lazy var deleteAccountCell: ActionCell = {
  115. let cell = ActionCell(frame: .zero)
  116. cell.actionTitle = String.localized("delete_account")
  117. cell.actionColor = UIColor.red
  118. cell.tag = tagDeleteAccountCell
  119. return cell
  120. }()
  121. lazy var advancedShowCell: UITableViewCell = {
  122. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  123. cell.textLabel?.text = String.localized("menu_advanced")
  124. cell.accessoryType = .disclosureIndicator
  125. cell.tag = tagAdvancedCell
  126. return cell
  127. }()
  128. lazy var imapServerCell: TextFieldCell = {
  129. let cell = TextFieldCell(
  130. descriptionID: "login_imap_server",
  131. placeholder: String.localized("automatic"),
  132. delegate: self)
  133. cell.tag = tagImapServerCell
  134. cell.setText(text: dcContext.mailServer ?? nil)
  135. cell.textField.tag = tagTextFieldImapServer
  136. cell.textField.autocorrectionType = .no
  137. cell.textField.spellCheckingType = .no
  138. cell.textField.autocapitalizationType = .none
  139. return cell
  140. }()
  141. lazy var imapUserCell: TextFieldCell = {
  142. let cell = TextFieldCell(
  143. descriptionID: "login_imap_login",
  144. placeholder: String.localized("automatic"),
  145. delegate: self)
  146. cell.setText(text: dcContext.mailUser ?? nil)
  147. cell.textField.tag = tagTextFieldImapLogin
  148. cell.tag = tagImapUserCell
  149. return cell
  150. }()
  151. func editablePort(port: String?) -> String {
  152. if let port = port {
  153. if Int(port) == 0 {
  154. return ""
  155. }
  156. return port
  157. } else {
  158. return ""
  159. }
  160. }
  161. lazy var imapPortCell: TextFieldCell = {
  162. let cell = TextFieldCell(
  163. descriptionID: "login_imap_port",
  164. placeholder: String.localized("automatic"),
  165. delegate: self)
  166. cell.tag = tagImapPortCell
  167. cell.setText(text: editablePort(port: dcContext.mailPort))
  168. cell.textField.tag = tagImapPortCell
  169. cell.textField.keyboardType = .numberPad
  170. return cell
  171. }()
  172. lazy var imapSecurityCell: UITableViewCell = {
  173. let text = "\(dcContext.getImapSecurity())"
  174. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  175. cell.textLabel?.text = String.localized("login_imap_security")
  176. cell.accessoryType = .disclosureIndicator
  177. cell.detailTextLabel?.text = "\(dcContext.getImapSecurity())"
  178. cell.selectionStyle = .none
  179. cell.tag = tagImapSecurityCell
  180. return cell
  181. }()
  182. lazy var smtpServerCell: TextFieldCell = {
  183. let cell = TextFieldCell(
  184. descriptionID: "login_smtp_server",
  185. placeholder: String.localized("automatic"),
  186. delegate: self)
  187. cell.textField.tag = tagTextFieldSmtpServer
  188. cell.setText(text: dcContext.sendServer ?? nil)
  189. cell.tag = tagSmtpServerCell
  190. cell.textField.autocorrectionType = .no
  191. cell.textField.spellCheckingType = .no
  192. cell.textField.autocapitalizationType = .none
  193. return cell
  194. }()
  195. lazy var smtpUserCell: TextFieldCell = {
  196. let cell = TextFieldCell(
  197. descriptionID: "login_smtp_login",
  198. placeholder: String.localized("automatic"),
  199. delegate: self)
  200. cell.textField.tag = tagTextFieldSmtpUser
  201. cell.setText(text: dcContext.sendUser ?? nil)
  202. cell.tag = tagSmtpUserCell
  203. return cell
  204. }()
  205. lazy var smtpPortCell: TextFieldCell = {
  206. let cell = TextFieldCell(
  207. descriptionID: "login_smtp_port",
  208. placeholder: String.localized("automatic"),
  209. delegate: self)
  210. cell.tag = tagSmtpPortCell
  211. cell.setText(text: editablePort(port: dcContext.sendPort))
  212. cell.textField.tag = tagSmtpPortCell
  213. cell.textField.keyboardType = .numberPad
  214. return cell
  215. }()
  216. lazy var smtpPasswordCell: TextFieldCell = {
  217. let cell = TextFieldCell(
  218. descriptionID: "login_smtp_password",
  219. placeholder: String.localized("automatic"),
  220. delegate: self)
  221. cell.textField.textContentType = UITextContentType.password
  222. cell.setText(text: dcContext.sendPw ?? nil)
  223. cell.textField.isSecureTextEntry = true
  224. cell.textField.tag = tagTextFieldSmtpPassword
  225. cell.tag = tagSmtpPasswordCell
  226. return cell
  227. }()
  228. lazy var smtpSecurityCell: UITableViewCell = {
  229. let security = "\(dcContext.getSmtpSecurity())"
  230. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  231. cell.textLabel?.text = String.localized("login_smtp_security")
  232. cell.detailTextLabel?.text = security
  233. cell.tag = tagSmtpSecurityCell
  234. cell.accessoryType = .disclosureIndicator
  235. cell.selectionStyle = .none
  236. return cell
  237. }()
  238. lazy var certCheckCell: UITableViewCell = {
  239. let certCheckType = CertificateCheckController.ValueConverter.convertHexToString(value: dcContext.certificateChecks)
  240. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  241. cell.textLabel?.text = String.localized("login_certificate_checks")
  242. cell.detailTextLabel?.text = certCheckType
  243. cell.tag = tagCertCheckCell
  244. cell.accessoryType = .disclosureIndicator
  245. cell.selectionStyle = .none
  246. return cell
  247. }()
  248. lazy var inboxWatchCell: SwitchCell = {
  249. return SwitchCell(
  250. textLabel: String.localized("pref_watch_inbox_folder"),
  251. on: dcContext.getConfigBool("inbox_watch"),
  252. action: { cell in
  253. self.dcContext.setConfigBool("inbox_watch", cell.isOn)
  254. })
  255. }()
  256. lazy var sentboxWatchCell: SwitchCell = {
  257. return SwitchCell(
  258. textLabel: String.localized("pref_watch_sent_folder"),
  259. on: dcContext.getConfigBool("sentbox_watch"),
  260. action: { cell in
  261. self.dcContext.setConfigBool("sentbox_watch", cell.isOn)
  262. })
  263. }()
  264. lazy var mvboxWatchCell: SwitchCell = {
  265. return SwitchCell(
  266. textLabel: String.localized("pref_watch_mvbox_folder"),
  267. on: dcContext.getConfigBool("mvbox_watch"),
  268. action: { cell in
  269. self.dcContext.setConfigBool("mvbox_watch", cell.isOn)
  270. })
  271. }()
  272. lazy var sendCopyToSelfCell: SwitchCell = {
  273. return SwitchCell(
  274. textLabel: String.localized("pref_send_copy_to_self"),
  275. on: dcContext.getConfigBool("bcc_self"),
  276. action: { cell in
  277. self.dcContext.setConfigBool("bcc_self", cell.isOn)
  278. })
  279. }()
  280. lazy var mvboxMoveCell: SwitchCell = {
  281. return SwitchCell(
  282. textLabel: String.localized("pref_auto_folder_moves"),
  283. on: dcContext.getConfigBool("mvbox_move"),
  284. action: { cell in
  285. self.dcContext.setConfigBool("mvbox_move", cell.isOn)
  286. })
  287. }()
  288. private lazy var loginButton: UIBarButtonItem = {
  289. let button = UIBarButtonItem(
  290. title: String.localized("login_title"),
  291. style: .done,
  292. target: self,
  293. action: #selector(loginButtonPressed))
  294. button.isEnabled = !dcContext.isConfigured()
  295. return button
  296. }()
  297. // MARK: - constructor
  298. init(dcContext: DcContext, editView: Bool) {
  299. self.editView = editView
  300. self.dcContext = dcContext
  301. self.sections.append(basicSection)
  302. self.sections.append(advancedSection)
  303. if editView {
  304. self.sections.append(folderSection)
  305. self.sections.append(dangerSection)
  306. } else {
  307. self.sections.append(restoreSection)
  308. }
  309. super.init(style: .grouped)
  310. hidesBottomBarWhenPushed = true
  311. }
  312. required init?(coder _: NSCoder) {
  313. fatalError("init(coder:) has not been implemented")
  314. }
  315. // MARK: - lifecycle
  316. override func viewDidLoad() {
  317. super.viewDidLoad()
  318. if editView {
  319. title = String.localized("pref_password_and_account_settings")
  320. } else {
  321. title = String.localized("login_header")
  322. }
  323. navigationItem.rightBarButtonItem = loginButton
  324. }
  325. override func viewWillAppear(_ animated: Bool) {
  326. super.viewWillAppear(animated)
  327. initSelectionCells()
  328. updateCellData()
  329. handleLoginButton()
  330. }
  331. override func viewDidAppear(_ animated: Bool) {
  332. super.viewDidAppear(animated)
  333. }
  334. override func viewWillDisappear(_ animated: Bool) {
  335. resignFirstResponderOnAllCells()
  336. }
  337. override func viewDidDisappear(_: Bool) {
  338. let nc = NotificationCenter.default
  339. if let backupProgressObserver = self.backupProgressObserver {
  340. nc.removeObserver(backupProgressObserver)
  341. }
  342. if let configureProgressObserver = self.configureProgressObserver {
  343. nc.removeObserver(configureProgressObserver)
  344. }
  345. if let oauth2Observer = self.oauth2Observer {
  346. nc.removeObserver(oauth2Observer)
  347. }
  348. }
  349. // MARK: - Table view data source
  350. override func numberOfSections(in _: UITableView) -> Int {
  351. return sections.count
  352. }
  353. override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
  354. if sections[section] == basicSection {
  355. return basicSectionCells.count
  356. } else if sections[section] == restoreSection {
  357. return restoreCells.count
  358. } else if sections[section] == folderSection {
  359. return folderCells.count
  360. } else if sections[section] == dangerSection {
  361. return dangerCells.count
  362. } else {
  363. return advancedSectionShowing ? advancedSectionCells.count : 1
  364. }
  365. }
  366. override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
  367. if sections[section] == basicSection && editView {
  368. return String.localized("login_header")
  369. } else if sections[section] == folderSection {
  370. return String.localized("pref_imap_folder_handling")
  371. } else if sections[section] == dangerSection {
  372. return String.localized("danger")
  373. } else {
  374. return nil
  375. }
  376. }
  377. override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
  378. if sections[section] == basicSection {
  379. return String.localized("login_no_servers_hint")
  380. } else if sections[section] == advancedSection {
  381. if advancedSectionShowing && dcContext.isConfigured() {
  382. var info = String.localized("used_settings") + "\n"
  383. let serverFlags = Int(dcContext.getConfig("configured_server_flags") ?? "") ?? 0
  384. info += "IMAP "
  385. info += SecurityConverter.convertHexToString(type: .IMAPSecurity, hex: serverFlags&0x700) + " "
  386. info += (dcContext.getConfig("configured_mail_user") ?? "unset") + ":***@"
  387. info += (dcContext.getConfig("configured_mail_server") ?? "unset") + ":"
  388. info += (dcContext.getConfig("configured_mail_port") ?? "unset") + "\n"
  389. info += "SMTP "
  390. info += SecurityConverter.convertHexToString(type: .SMTPSecurity, hex: serverFlags&0x70000) + " "
  391. info += (dcContext.getConfig("configured_send_user") ?? "unset") + ":***@"
  392. info += (dcContext.getConfig("configured_send_server") ?? "unset") + ":"
  393. info += (dcContext.getConfig("configured_send_port") ?? "unset") + "\n\n"
  394. info += String.localized("login_subheader")
  395. return info
  396. } else {
  397. return String.localized("login_subheader")
  398. }
  399. } else if sections[section] == folderSection {
  400. return String.localized("pref_auto_folder_moves_explain")
  401. } else {
  402. return nil
  403. }
  404. }
  405. override func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  406. let section = indexPath.section
  407. let row = indexPath.row
  408. if sections[section] == basicSection {
  409. return basicSectionCells[row]
  410. } else if sections[section] == restoreSection {
  411. return restoreCells[row]
  412. } else if sections[section] == folderSection {
  413. return folderCells[row]
  414. } else if sections[section] == dangerSection {
  415. return dangerCells[row]
  416. } else {
  417. return advancedSectionCells[row]
  418. }
  419. }
  420. override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  421. guard let tappedCell = tableView.cellForRow(at: indexPath) else { return }
  422. // handle tap on password -> show oAuthDialogue
  423. switch tappedCell.tag {
  424. case tagPasswordCell:
  425. if let textFieldCell = tappedCell as? TextFieldCell {
  426. if let emailAdress = textFieldCell.getText() {
  427. _ = showOAuthAlertIfNeeded(emailAddress: emailAdress, handleCancel: nil)
  428. }
  429. }
  430. case tagRestoreCell:
  431. tableView.reloadData() // otherwise the disclosureIndicator may stay selected
  432. restoreBackup()
  433. case tagEmptyServerCell:
  434. emptyServer()
  435. case tagDeleteAccountCell:
  436. deleteAccount()
  437. case tagAdvancedCell:
  438. toggleAdvancedSection()
  439. case tagImapSecurityCell:
  440. coordinator?.showImapSecurityOptions()
  441. case tagSmtpSecurityCell:
  442. coordinator?.showSmptpSecurityOptions()
  443. case tagCertCheckCell:
  444. coordinator?.showCertCheckOptions()
  445. default:
  446. break
  447. }
  448. }
  449. // MARK: - updates
  450. private func updateCellData() {
  451. emailCell.setText(text: dcContext.addr ?? nil)
  452. passwordCell.setText(text: dcContext.mailPw ?? nil)
  453. }
  454. // MARK: - actions
  455. private func toggleAdvancedSection() {
  456. let willShow = !advancedSectionShowing
  457. guard let advancedSectionIndex = sections.firstIndex(of: advancedSection) else { return }
  458. var advancedIndexPaths: [IndexPath] = advancedSectionCells.indices.map { IndexPath(row: $0, section: advancedSectionIndex) }
  459. advancedIndexPaths.removeFirst() // do not touch the first item that is the switch itself
  460. // on expansion, replace the disclosureIndicator by an n-dash
  461. advancedShowCell.accessoryType = willShow ? .none : .disclosureIndicator
  462. advancedShowCell.detailTextLabel?.text = willShow ? "\u{2013}" : nil
  463. advancedSectionShowing = willShow // set flag before delete/insert, because cellForRowAt will be triggered and uses this flag
  464. if willShow {
  465. tableView.insertRows(at: advancedIndexPaths, with: .fade)
  466. } else {
  467. tableView.deleteRows(at: advancedIndexPaths, with: .fade)
  468. }
  469. tableView.reloadData() // needed to force a redraw
  470. }
  471. @objc private func loginButtonPressed() {
  472. guard let emailAddress = emailCell.getText() else {
  473. return // handle case when either email or pw fields are empty
  474. }
  475. let oAuthStarted = showOAuthAlertIfNeeded(emailAddress: emailAddress, handleCancel: loginButtonPressed)
  476. // if canceled we will run this method again but this time oAuthStarted will be false
  477. if oAuthStarted {
  478. // the loginFlow will be handled by oAuth2
  479. return
  480. }
  481. let password = passwordCell.getText() ?? "" // empty passwords are ok -> for oauth there is no password needed
  482. login(emailAddress: emailAddress, password: password)
  483. }
  484. private func updateProviderInfo() {
  485. provider = dcContext.getProviderFromEmail(addr: emailCell.getText() ?? "")
  486. if let hint = provider?.beforeLoginHint,
  487. let status = provider?.status,
  488. let statusType = ProviderInfoStatus(rawValue: status),
  489. !hint.isEmpty {
  490. providerInfoCell.updateInfo(hint: hint, hintType: statusType)
  491. if !providerInfoShowing {
  492. showProviderInfo()
  493. }
  494. } else if providerInfoShowing {
  495. hideProviderInfo()
  496. }
  497. }
  498. private func showProviderInfo() {
  499. basicSectionCells = [emailCell, passwordCell, providerInfoCell]
  500. let providerInfoCellIndexPath = IndexPath(row: 2, section: 0)
  501. tableView.insertRows(at: [providerInfoCellIndexPath], with: .fade)
  502. providerInfoShowing = true
  503. }
  504. private func hideProviderInfo() {
  505. providerInfoCell.updateInfo(hint: nil, hintType: .none)
  506. basicSectionCells = [emailCell, passwordCell]
  507. let providerInfoCellIndexPath = IndexPath(row: 2, section: 0)
  508. tableView.deleteRows(at: [providerInfoCellIndexPath], with: .automatic)
  509. providerInfoShowing = false
  510. }
  511. private func login(emailAddress: String, password: String, skipAdvanceSetup: Bool = false) {
  512. addProgressHudLoginListener()
  513. resignFirstResponderOnAllCells() // this will resign focus from all textFieldCells so the keyboard wont pop up anymore
  514. dcContext.addr = emailAddress
  515. dcContext.mailPw = password
  516. if !skipAdvanceSetup {
  517. evaluateAdvancedSetup() // this will set MRConfig related to advanced fields
  518. }
  519. print("oAuth-Flag when loggin in: \(dcContext.getAuthFlags())")
  520. dcContext.configure()
  521. showProgressAlert(title: String.localized("login_header"))
  522. }
  523. @objc func closeButtonPressed() {
  524. dismiss(animated: true, completion: nil)
  525. }
  526. // returns true if needed
  527. private func showOAuthAlertIfNeeded(emailAddress: String, handleCancel: (() -> Void)?) -> Bool {
  528. return false
  529. // disable oauth2 for now as not yet supported by deltachat-rust.
  530. /*
  531. if skipOauth {
  532. // user has previously denied oAuth2-setup
  533. return false
  534. }
  535. guard let oAuth2UrlPointer = dc_get_oauth2_url(mailboxPointer, emailAddress, "chat.delta:/auth") else {
  536. //MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL)) -- do not reset, there may be different values
  537. return false
  538. }
  539. let oAuth2Url = String(cString: oAuth2UrlPointer)
  540. if let url = URL(string: oAuth2Url) {
  541. let title = "Continue with simplified setup"
  542. // swiftlint:disable all
  543. let message = "The entered e-mail address supports a simplified setup (oAuth2).\n\nIn the next step, please allow Delta Chat to act as your Chat with E-Mail app.\n\nThere are no Delta Chat servers, your data stays on your device."
  544. let oAuthAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
  545. let confirm = UIAlertAction(title: "Confirm", style: .default, handler: {
  546. [unowned self] _ in
  547. let nc = NotificationCenter.default
  548. self.oauth2Observer = nc.addObserver(self, selector: #selector(self.oauthLoginApproved), name: NSNotification.Name("oauthLoginApproved"), object: nil)
  549. self.launchOAuthBrowserWindow(url: url)
  550. })
  551. let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: {
  552. _ in
  553. MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL))
  554. self.skipOauth = true
  555. handleCancel?()
  556. })
  557. oAuthAlertController.addAction(confirm)
  558. oAuthAlertController.addAction(cancel)
  559. present(oAuthAlertController, animated: true, completion: nil)
  560. return true
  561. } else {
  562. return false
  563. }
  564. */
  565. }
  566. @objc func oauthLoginApproved(notification: Notification) {
  567. guard let userInfo = notification.userInfo, let token = userInfo["token"] as? String, let emailAddress = emailCell.getText() else {
  568. return
  569. }
  570. passwordCell.setText(text: token)
  571. dcContext.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
  572. login(emailAddress: emailAddress, password: token, skipAdvanceSetup: true)
  573. }
  574. private func launchOAuthBrowserWindow(url: URL) {
  575. UIApplication.shared.open(url) // this opens safari as seperate app
  576. }
  577. private func addProgressHudLoginListener() {
  578. let nc = NotificationCenter.default
  579. configureProgressObserver = nc.addObserver(
  580. forName: dcNotificationConfigureProgress,
  581. object: nil,
  582. queue: nil
  583. ) {
  584. notification in
  585. if let ui = notification.userInfo {
  586. if ui["error"] as! Bool {
  587. self.updateProgressAlert(error: ui["errorMessage"] as? String)
  588. } else if ui["done"] as! Bool {
  589. self.updateProgressAlertSuccess(completion: self.handleLoginSuccess)
  590. } else {
  591. self.updateProgressAlertValue(value: ui["progress"] as? Int)
  592. }
  593. }
  594. }
  595. }
  596. private func addProgressHudBackupListener() {
  597. let nc = NotificationCenter.default
  598. backupProgressObserver = nc.addObserver(
  599. forName: dcNotificationImexProgress,
  600. object: nil,
  601. queue: nil
  602. ) {
  603. notification in
  604. if let ui = notification.userInfo {
  605. if ui["error"] as! Bool {
  606. self.updateProgressAlert(error: ui["errorMessage"] as? String)
  607. } else if ui["done"] as! Bool {
  608. self.updateProgressAlertSuccess(completion: self.handleLoginSuccess)
  609. } else {
  610. self.updateProgressAlertValue(value: ui["progress"] as? Int)
  611. }
  612. }
  613. }
  614. }
  615. private func evaluateAdvancedSetup() {
  616. for cell in advancedSectionCells {
  617. if let textFieldCell = cell as? TextFieldCell {
  618. switch textFieldCell.tag {
  619. case tagImapServerCell:
  620. dcContext.mailServer = textFieldCell.getText() ?? nil
  621. case tagImapPortCell:
  622. dcContext.mailPort = textFieldCell.getText() ?? nil
  623. case tagImapUserCell:
  624. dcContext.mailUser = textFieldCell.getText() ?? nil
  625. case tagSmtpServerCell:
  626. dcContext.sendServer = textFieldCell.getText() ?? nil
  627. case tagSmtpPortCell:
  628. dcContext.sendPort = textFieldCell.getText() ?? nil
  629. case tagSmtpUserCell:
  630. dcContext.sendUser = textFieldCell.getText() ?? nil
  631. case tagSmtpPasswordCell:
  632. dcContext.sendPw = textFieldCell.getText() ?? nil
  633. default:
  634. logger.info("unknown identifier \(cell.tag)")
  635. }
  636. }
  637. }
  638. }
  639. private func restoreBackup() {
  640. logger.info("restoring backup")
  641. if dcContext.configured {
  642. return
  643. }
  644. addProgressHudBackupListener()
  645. let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
  646. if !documents.isEmpty {
  647. logger.info("looking for backup in: \(documents[0])")
  648. if let file = dcContext.imexHasBackup(filePath: documents[0]) {
  649. logger.info("restoring backup: \(file)")
  650. showProgressAlert(title: String.localized("import_backup_title"))
  651. dcContext.imex(what: DC_IMEX_IMPORT_BACKUP, directory: file)
  652. }
  653. else {
  654. let alert = UIAlertController(
  655. title: String.localized("import_backup_title"),
  656. message: String.localizedStringWithFormat(
  657. String.localized("import_backup_no_backup_found"),
  658. "iTunes / <Your Device> / File Sharing / Delta Chat"), // TOOD: maybe better use an iOS-specific string here
  659. preferredStyle: .alert)
  660. alert.addAction(UIAlertAction(title: String.localized("ok"), style: .cancel))
  661. present(alert, animated: true)
  662. }
  663. }
  664. logger.error("no documents directory found")
  665. }
  666. private func emptyServer() {
  667. let alert = UIAlertController(title: String.localized("pref_empty_server_title"),
  668. message: String.localized("pref_empty_server_msg"), preferredStyle: .safeActionSheet)
  669. alert.addAction(UIAlertAction(title: String.localized("pref_empty_server_inbox"), style: .destructive, handler: { _ in
  670. self.emptyServer2ndConfirm(title: String.localized("pref_empty_server_inbox"), flags: Int(DC_EMPTY_INBOX))
  671. }))
  672. alert.addAction(UIAlertAction(title: String.localized("pref_empty_server_mvbox"), style: .destructive, handler: { _ in
  673. self.emptyServer2ndConfirm(title: String.localized("pref_empty_server_mvbox"), flags: Int(DC_EMPTY_MVBOX))
  674. }))
  675. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel))
  676. present(alert, animated: true, completion: nil)
  677. }
  678. private func emptyServer2ndConfirm(title: String, flags: Int) {
  679. let alert = UIAlertController(
  680. title: title,
  681. message: String.localized("pref_empty_server_msg"), preferredStyle: .alert)
  682. alert.addAction(UIAlertAction(title: String.localized("pref_empty_server_do_button"), style: .destructive, handler: { _ in
  683. self.dcContext.emptyServer(flags: flags)
  684. }))
  685. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel))
  686. present(alert, animated: true, completion: nil)
  687. }
  688. private func deleteAccount() {
  689. guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
  690. return
  691. }
  692. let alert = UIAlertController(
  693. title: String.localized("delete_account_ask"),
  694. message: nil,
  695. preferredStyle: .safeActionSheet)
  696. alert.addAction(UIAlertAction(title: String.localized("delete_account"), style: .destructive, handler: { _ in
  697. appDelegate.stop()
  698. appDelegate.close()
  699. DatabaseHelper().clearAccountData()
  700. appDelegate.open()
  701. appDelegate.start()
  702. appDelegate.appCoordinator.presentWelcomeController()
  703. }))
  704. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel))
  705. present(alert, animated: true, completion: nil)
  706. }
  707. private func handleLoginSuccess() {
  708. // used when login hud successfully went through
  709. let appDelegate = UIApplication.shared.delegate as! AppDelegate
  710. appDelegate.registerForPushNotifications()
  711. initSelectionCells();
  712. if let onLoginSuccess = self.coordinator?.onLoginSuccess {
  713. onLoginSuccess()
  714. } else {
  715. self.coordinator?.navigateBack()
  716. }
  717. }
  718. private func initSelectionCells() {
  719. smtpSecurityCell.detailTextLabel?.text = SecurityConverter.convertHexToString(type: .SMTPSecurity, hex: dcContext.getSmtpSecurity())
  720. imapSecurityCell.detailTextLabel?.text = SecurityConverter.convertHexToString(type: .IMAPSecurity, hex: dcContext.getImapSecurity())
  721. certCheckCell.detailTextLabel?.text = CertificateCheckController.ValueConverter.convertHexToString(value: dcContext.certificateChecks)
  722. }
  723. private func resignFirstResponderOnAllCells() {
  724. let _ = basicSectionCells.map({
  725. resignCell(cell: $0)
  726. })
  727. let _ = advancedSectionCells.map({
  728. resignCell(cell: $0)
  729. })
  730. }
  731. private func handleLoginButton() {
  732. loginButton.isEnabled = !(emailCell.getText() ?? "").isEmpty && !(passwordCell.getText() ?? "").isEmpty
  733. }
  734. private func handleProviderInfoButton() {
  735. guard let provider = provider else {
  736. return
  737. }
  738. coordinator?.openProviderInfo(provider: provider)
  739. }
  740. func resignCell(cell: UITableViewCell) {
  741. if let c = cell as? TextFieldCell {
  742. c.textField.resignFirstResponder()
  743. }
  744. }
  745. @objc private func textFieldDidChange() {
  746. handleLoginButton()
  747. }
  748. @objc private func emailCellEdited() {
  749. if providerInfoShowing {
  750. updateProviderInfo()
  751. }
  752. }
  753. }
  754. // MARK: - UITextFieldDelegate
  755. extension AccountSetupController: UITextFieldDelegate {
  756. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  757. let currentTag = textField.tag
  758. if let nextField = tableView.viewWithTag(currentTag + 100) as? UITextField {
  759. if nextField.tag > tagTextFieldPassword, !advancedSectionShowing {
  760. // gets here when trying to activate a collapsed cell
  761. return false
  762. }
  763. nextField.becomeFirstResponder()
  764. return true
  765. } else {
  766. textField.resignFirstResponder()
  767. emailCell.textField.becomeFirstResponder()
  768. let indexPath = IndexPath(row: 0, section: 0)
  769. tableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.top, animated: true)
  770. return true
  771. }
  772. }
  773. func textFieldDidBeginEditing(_ textField: UITextField) {
  774. if textField.tag == tagTextFieldEmail {
  775. // this will re-enable possible oAuth2-login
  776. skipOauth = false
  777. }
  778. }
  779. func textFieldDidEndEditing(_ textField: UITextField) {
  780. if textField.tag == tagTextFieldEmail {
  781. let _ = showOAuthAlertIfNeeded(emailAddress: textField.text ?? "", handleCancel: {
  782. self.passwordCell.textField.becomeFirstResponder()
  783. })
  784. updateProviderInfo()
  785. }
  786. }
  787. }