AccountSetupController.swift 36 KB

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