Bastian van de Wetering před 6 roky
rodič
revize
d38551bf1d

+ 1 - 1
.swiftformat

@@ -1,3 +1,3 @@
 --indent 2
 
---exclude Pods,Generated
+--exclude Pods,Generated

+ 560 - 577
deltachat-ios/AccountSetupController.swift

@@ -6,594 +6,577 @@
 //  Copyright © 2019 Jonas Reinsch. All rights reserved.
 //
 
-import UIKit
 import SafariServices
+import UIKit
 
 class AccountSetupController: UITableViewController {
-
-	private var backupProgressObserver: Any?
-	private var configureProgressObserver: Any?
-	private var oauth2Observer: Any?
-
-	private lazy var hudHandler: HudHandler = {
-		let hudHandler = HudHandler(parentView: self.tableView)
-		return hudHandler
-	}()
-
-	private lazy var emailCell: TextFieldCell = {
-		let cell = TextFieldCell.makeEmailCell(delegate: self)
-		cell.textField.tag = 0
-		cell.textField.accessibilityIdentifier = "emailTextField"	// will be used to eventually show oAuth-Dialogue when pressing return key
-		cell.setText(text: MRConfig.addr ?? nil)
-		return cell
-	}()
-
-	private lazy var passwordCell: TextFieldCell = {
-		let cell = TextFieldCell.makePasswordCell(delegate: self)
-		cell.textField.tag = 1
-		cell.accessibilityIdentifier = "passwordCell"	// will be used to eventually show oAuth-Dialogue when selecting
-		cell.setText(text: MRConfig.mailPw ?? nil)
-		return cell
-	}()
-
-	/*
-	Advanced Cells:
-	IMAP Server, IMAP User, IMAP Port, IMAP Security, SMTP Server, SMTP User, SMTP Port, SMTP Password, SMTP Security
-	*/
-	/*
-	lazy var imapServerCell = TextFieldCell(description: "IMAP Server", placeholder: MRConfig.mailServer ?? MRConfig.configuredMailServer)
-	lazy var imapUserCell = TextFieldCell(description: "IMAP User", placeholder: MRConfig.mailUser ?? MRConfig.configuredMailUser)
-	lazy var imapPortCell = TextFieldCell(description: "IMAP Port", placeholder: MRConfig.mailPort ?? MRConfig.configuredMailPort)
-	lazy var imapSecurityCell = TextFieldCell(description: "IMAP Security", placeholder: "to do")
-
-	lazy var smtpServerCell = TextFieldCell(description: "SMTP Server", placeholder: MRConfig.sendServer ?? MRConfig.configuredSendServer)
-	lazy var smtpUserCell = TextFieldCell(description: "SMTP User", placeholder: MRConfig.sendUser ?? MRConfig.configuredSendUser)
-	lazy var smtpPortCell = TextFieldCell(description: "SMTP Port", placeholder: MRConfig.sendPort ?? MRConfig.configuredSendPort)
-	lazy var smtpPasswordCell = TextFieldCell(description: "SMTP Password", placeholder: "*************")
-	lazy var smtpSecurityCell = TextFieldCell(description: "SMTP Security", placeholder: "to do")
-	*/
-
-	// TODO: consider adding delegates and tags by loop - leave for now like this
-	lazy var imapServerCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "IMAP Server", placeholder: MRConfig.mailServer ?? MRConfig.configuredMailServer, delegate: self)
-		cell.accessibilityIdentifier = "IMAPServerCell"
-		cell.textField.tag = 2
-		return cell
-	}()
-
-	lazy var imapUserCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "IMAP User", placeholder: MRConfig.mailUser ?? MRConfig.configuredMailUser, delegate: self)
-		cell.accessibilityIdentifier = "IMAPUserCell"
-		cell.textField.tag = 3
-		return cell
-	}()
-
-	lazy var imapPortCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "IMAP Port", placeholder: MRConfig.mailPort ?? MRConfig.configuredMailPort, delegate: self)
-		cell.accessibilityIdentifier = "IMAPPortCell"
-		cell.textField.tag = 4
-		return cell
-	}()
-
-	lazy var imapSecurityCell: TextFieldCell = {
-		let text = "\(MRConfig.getImapSecurity())"
-		let cell = TextFieldCell(description: "IMAP Security", placeholder: text, delegate: self)
-		cell.accessibilityIdentifier = "IMAPSecurityCell"
-		cell.textField.tag = 5
-		cell.textField.keyboardType = UIKeyboardType.numberPad
-		return cell
-	}()
-
-	lazy var smtpServerCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "SMTP Server", placeholder: MRConfig.sendServer ?? MRConfig.configuredSendServer, delegate: self)
-		cell.accessibilityIdentifier = "SMTPServerCell"
-		cell.textField.tag = 6
-		return cell
-	}()
-	lazy var smtpUserCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "SMTP User", placeholder: MRConfig.sendUser ?? MRConfig.configuredSendUser, delegate: self)
-		cell.accessibilityIdentifier = "SMTPUserCell"
-		cell.textField.tag = 7
-		return cell
-	}()
-	lazy var smtpPortCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "SMTP Port", placeholder: MRConfig.sendPort ?? MRConfig.configuredSendPort, delegate: self)
-		cell.accessibilityIdentifier = "SMTPPortCell"
-		cell.textField.tag = 8
-		return cell
-	}()
-	lazy var smtpPasswordCell: TextFieldCell = {
-		let cell = TextFieldCell(description: "SMTP Password", placeholder: "*************", delegate: self)
-		cell.accessibilityIdentifier = "SMTPPasswordCell"
-		cell.textField.tag = 9
-		return cell
-	}()
-
-	lazy var smtpSecurityCell: TextFieldCell = {
-		let text = "\(MRConfig.getSmtpSecurity())"
-		let cell = TextFieldCell(description: "SMTP Security", placeholder: text, delegate: self)
-		cell.accessibilityIdentifier = "SMTPSecurityCell"
-		cell.textField.tag = 10
-		cell.textField.keyboardType = UIKeyboardType.numberPad
-		return cell
-	}()
-
-	// this loginButton can be enabled and disabled
-	let loginButton: UIBarButtonItem = UIBarButtonItem(title: "Login", style: .done, target: self, action: #selector(loginButtonPressed))
-
-	private lazy var basicSectionCells: [UITableViewCell] = [emailCell, passwordCell]
-	private lazy var advancedSectionCells: [UITableViewCell] = [
-		imapServerCell,
-		imapUserCell,
-		imapPortCell,
-		imapSecurityCell,
-		smtpServerCell,
-		smtpUserCell,
-		smtpPortCell,
-		smtpPasswordCell,
-		smtpSecurityCell
-	]
-
-	private var advancedSectionShowing: Bool = false
-
-	init() {
-		super.init(style: .grouped)
-	}
-
-	required init?(coder aDecoder: NSCoder) {
-		fatalError("init(coder:) has not been implemented")
-	}
-
-	override func viewDidLoad() {
-		super.viewDidLoad()
-		self.title = "Login to your server"
-		self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(closeButtonPressed))
-		self.navigationItem.rightBarButtonItem = loginButton
-	}
-
-	override func viewDidAppear(_ animated: Bool) {
-		super.viewDidAppear(animated)
-		addProgressHudEventListener()
-		//loginButton.isEnabled = false
-		print(emailCell.getText())
-		print("account setup appeared")
-	}
-
-	override func viewDidDisappear(_ animated: Bool) {
-		let nc = NotificationCenter.default
-		if let backupProgressObserver = self.backupProgressObserver {
-			nc.removeObserver(backupProgressObserver)
-		}
-		if let configureProgressObserver = self.configureProgressObserver {
-			nc.removeObserver(configureProgressObserver)
-		}
-		if let oauth2Observer = self.oauth2Observer {
-			nc.removeObserver(oauth2Observer)
-		}
-	}
-
-	// MARK: - Table view data source
-
-	override func numberOfSections(in tableView: UITableView) -> Int {
-		// #warning Incomplete implementation, return the number of sections
-		return 2
-	}
-
-	override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-		// #warning Incomplete implementation, return the number of rows
-		if section == 0 {
-			return basicSectionCells.count
-		} else {
-			return advancedSectionShowing ? advancedSectionCells.count : 0
-		}
-	}
-
-	override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
-		if section == 1 {
-			return "Advanced"
-		} else {
-			return nil
-		}
-	}
-
-	override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
-
-		if section == 1 {
-			// Advanced Header
-			let advancedView = AdvancedSectionHeader()
-			advancedView.handleTap = toggleAdvancedSection
-			// set tapHandler
-			return advancedView
-
-		} else {
-			return nil
-		}
-	}
-
-	override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
-		return 36.0
-	}
-
-	override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
-		if section == 0 {
-			return "There are no Delta Chat servers, your data stays on your device!"
-		} else {
-			return "For known email providers additional settings are setup automatically. Sometimes IMAP needs to be enabled in the web frontend. Consult your email provider or friends for help"
-		}
-	}
-
-	override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-		let section = indexPath.section
-		let row = indexPath.row
-
-		if section == 0 {
-			// basicSection
-			return basicSectionCells[row]
-		} else {
-			// advancedSection
-			return advancedSectionCells[row]
-		}
-	}
-
-	override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-		// handle tap on password -> show eventuall oAuthDialogue
-		if let cell = tableView.cellForRow(at: indexPath) as? TextFieldCell {
-			if cell.accessibilityIdentifier == "passwordCell" {
-				if let emailAdress = cell.getText() {
-					_ = showOAuthAlertIfNeeded(emailAddress: emailAdress, handleCancel: nil)
-				}
-			}
-		}
-	}
-
-	private func toggleAdvancedSection(button: UILabel) {
-
-		let willShow = !self.advancedSectionShowing
-
-		// extract indexPaths from advancedCells
-		let advancedIndexPaths: [IndexPath] = advancedSectionCells.indices.map({IndexPath(row: $0, section: 1)})
-
-		//advancedSectionCells.indices.map({indexPaths.append(IndexPath(row: $0, section: 1))}
-
-		// set flag before delete/insert operation, because cellForRowAt will be triggered and uses this flag
-		self.advancedSectionShowing = willShow
-
-		button.text = willShow ? "Hide":"Show"
-
-		if willShow {
-			tableView.insertRows(at: advancedIndexPaths, with: .fade)
-		} else {
-			tableView.deleteRows(at: advancedIndexPaths, with: .fade)
-
-		}
-
-	}
-
-	@objc private func loginButtonPressed() {
-
-		guard let emailAddress = emailCell.getText() else {
-			return // handle case when either email or pw fields are empty
-		}
-
-		let oAuthStarted = showOAuthAlertIfNeeded(emailAddress: emailAddress, handleCancel: self.loginButtonPressed)	// if canceled we will run this method again but this time oAuthStarted will be false
-
-		if oAuthStarted {
-			// the loginFlow will be handled by oAuth2
-			return
-		}
-
-		let password = passwordCell.getText()  ?? "" // empty passwords are ok -> for oauth there is no password needed
-		login(emailAddress: emailAddress, password: password)
-
-	}
-
-	private func login(emailAddress: String, password: String, skipAdvanceSetup: Bool = false) {
-		MRConfig.addr = emailAddress
-		MRConfig.mailPw = password
-		if skipAdvanceSetup {
-			evaluluateAdvancedSetup()	// this will set MRConfig related to advanced fields
-		}
-		dc_configure(mailboxPointer)
-		hudHandler.showBackupHud("Configuring account")
-	}
-
-	@objc func closeButtonPressed() {
-		dismiss(animated: true, completion: nil)
-	}
-
-	// returns true if needed
-	private func showOAuthAlertIfNeeded(emailAddress: String, handleCancel: (() -> Void)?) -> Bool {
-
-		if MRConfig.getAuthFlags() == 4 {
-			// user has previously denied oAuth2-setup
-			return false
-		}
-
-		guard let oAuth2UrlPointer = dc_get_oauth2_url(mailboxPointer, emailAddress, "chat.delta:/auth") else {
-			return false
-		}
-
-		let oAuth2Url = String(cString: oAuth2UrlPointer)
-
-		if let url = URL.init(string: oAuth2Url) {
-			let title = "Continue with simplified setup"
-			//swiftlint:disable all
-			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."
-
-			let oAuthAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
-			let confirm = UIAlertAction(title: "Confirm", style: .default, handler: {
-				[unowned self] _ in
-				let nc = NotificationCenter.default
-				self.oauth2Observer = nc.addObserver(self, selector: #selector(self.oauthLoginApproved), name: NSNotification.Name.init("oauthLoginApproved"), object: nil)
-				self.launchOAuthBrowserWindow(url: url)
-			})
-			let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: {
-				_ in
-				MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL))
-				handleCancel?()
-			})
-			oAuthAlertController.addAction(confirm)
-			oAuthAlertController.addAction(cancel)
-
-			present(oAuthAlertController, animated: true, completion: nil)
-			return true
-		} else {
-			return false
-		}
-	}
-
-	@objc func oauthLoginApproved(notification: Notification) {
-
-		guard let userInfo = notification.userInfo, let token = userInfo["token"] as? String, let emailAddress = emailCell.getText() else {
-			return
-			
-		}
-		passwordCell.setText(text: token)
-		MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
-		login(emailAddress: emailAddress, password: token, skipAdvanceSetup: true)
-	}
-
-	private func launchOAuthBrowserWindow(url: URL) {
-		UIApplication.shared.open(url) // this opens safari as seperate app
-	}
-
-	private func addProgressHudEventListener() {
-		let nc = NotificationCenter.default
-		backupProgressObserver = nc.addObserver(
-			forName: dcNotificationBackupProgress,
-			object: nil,
-			queue: nil
-		) {
-			notification in
-			if let ui = notification.userInfo {
-				if ui["error"] as! Bool {
-					self.hudHandler.setHudError(ui["errorMessage"] as? String)
-				} else if ui["done"] as! Bool {
-					self.hudHandler.setHudDone(callback: self.handleLoginSuccess )
-				} else {
-					self.hudHandler.setHudProgress(ui["progress"] as! Int)
-				}
-			}
-		}
-		configureProgressObserver = nc.addObserver(
-			forName: dcNotificationConfigureProgress,
-			object: nil,
-			queue: nil
-		) {
-			notification in
-			if let ui = notification.userInfo {
-				if ui["error"] as! Bool {
-					self.hudHandler.setHudError(ui["errorMessage"] as? String)
-				} else if ui["done"] as! Bool {
-					self.hudHandler.setHudDone(callback: self.handleLoginSuccess)
-				} else {
-					self.hudHandler.setHudProgress(ui["progress"] as! Int)
-				}
-			}
-		}
-	}
-
-	private func evaluluateAdvancedSetup() {
-
-		for cell in advancedSectionCells {
-
-			if let textFieldCell = cell as? TextFieldCell {
-				switch cell.accessibilityIdentifier {
-				case "IMAPServerCell":
-					MRConfig.mailServer = textFieldCell.getText() ?? nil
-				case "IMAPUserCell":
-					MRConfig.mailUser = textFieldCell.getText() ?? nil
-				case "IMAPPortCell":
-					MRConfig.mailPort = textFieldCell.getText() ?? nil
-				case "IMAPSecurityCell":
-					let flag = 0
-					MRConfig.setImapSecurity(imapFlags: flag)
-					break;
-				case "SMTPServerCell":
-					MRConfig.sendServer = textFieldCell.getText() ?? nil
-				case "SMTPSUserCell":
-					MRConfig.sendUser = textFieldCell.getText() ?? nil
-				case "SMTPPortCell":
-					MRConfig.sendPort = textFieldCell.getText() ?? nil
-				case "SMTPPasswordCell":
-					MRConfig.sendPw = textFieldCell.getText() ?? nil
-				case "SMTPSecurityCell":
-					let flag = 0
-					MRConfig.setSmtpSecurity(smptpFlags: flag)
-				default:
-					logger.info("unknown identifier", cell.accessibilityIdentifier ?? "")
-				}
-			}
-		}
-	}
-
-	private func handleLoginSuccess() {
-		// used when login hud successfully went trough
-		self.dismiss(animated: true, completion: nil)
-	}
+  private var backupProgressObserver: Any?
+  private var configureProgressObserver: Any?
+  private var oauth2Observer: Any?
+
+  private lazy var hudHandler: HudHandler = {
+    let hudHandler = HudHandler(parentView: self.tableView)
+    return hudHandler
+  }()
+
+  private lazy var emailCell: TextFieldCell = {
+    let cell = TextFieldCell.makeEmailCell(delegate: self)
+    cell.textField.tag = 0
+    cell.textField.accessibilityIdentifier = "emailTextField" // will be used to eventually show oAuth-Dialogue when pressing return key
+    cell.setText(text: MRConfig.addr ?? nil)
+    return cell
+  }()
+
+  private lazy var passwordCell: TextFieldCell = {
+    let cell = TextFieldCell.makePasswordCell(delegate: self)
+    cell.textField.tag = 1
+    cell.accessibilityIdentifier = "passwordCell" // will be used to eventually show oAuth-Dialogue when selecting
+    cell.setText(text: MRConfig.mailPw ?? nil)
+    return cell
+  }()
+
+  /*
+   Advanced Cells:
+   IMAP Server, IMAP User, IMAP Port, IMAP Security, SMTP Server, SMTP User, SMTP Port, SMTP Password, SMTP Security
+   */
+  /*
+   lazy var imapServerCell = TextFieldCell(description: "IMAP Server", placeholder: MRConfig.mailServer ?? MRConfig.configuredMailServer)
+   lazy var imapUserCell = TextFieldCell(description: "IMAP User", placeholder: MRConfig.mailUser ?? MRConfig.configuredMailUser)
+   lazy var imapPortCell = TextFieldCell(description: "IMAP Port", placeholder: MRConfig.mailPort ?? MRConfig.configuredMailPort)
+   lazy var imapSecurityCell = TextFieldCell(description: "IMAP Security", placeholder: "to do")
+
+   lazy var smtpServerCell = TextFieldCell(description: "SMTP Server", placeholder: MRConfig.sendServer ?? MRConfig.configuredSendServer)
+   lazy var smtpUserCell = TextFieldCell(description: "SMTP User", placeholder: MRConfig.sendUser ?? MRConfig.configuredSendUser)
+   lazy var smtpPortCell = TextFieldCell(description: "SMTP Port", placeholder: MRConfig.sendPort ?? MRConfig.configuredSendPort)
+   lazy var smtpPasswordCell = TextFieldCell(description: "SMTP Password", placeholder: "*************")
+   lazy var smtpSecurityCell = TextFieldCell(description: "SMTP Security", placeholder: "to do")
+   */
+
+  // TODO: consider adding delegates and tags by loop - leave for now like this
+  lazy var imapServerCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "IMAP Server", placeholder: MRConfig.mailServer ?? MRConfig.configuredMailServer, delegate: self)
+    cell.accessibilityIdentifier = "IMAPServerCell"
+    cell.textField.tag = 2
+    return cell
+  }()
+
+  lazy var imapUserCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "IMAP User", placeholder: MRConfig.mailUser ?? MRConfig.configuredMailUser, delegate: self)
+    cell.accessibilityIdentifier = "IMAPUserCell"
+    cell.textField.tag = 3
+    return cell
+  }()
+
+  lazy var imapPortCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "IMAP Port", placeholder: MRConfig.mailPort ?? MRConfig.configuredMailPort, delegate: self)
+    cell.accessibilityIdentifier = "IMAPPortCell"
+    cell.textField.tag = 4
+    return cell
+  }()
+
+  lazy var imapSecurityCell: TextFieldCell = {
+    let text = "\(MRConfig.getImapSecurity())"
+    let cell = TextFieldCell(description: "IMAP Security", placeholder: text, delegate: self)
+    cell.accessibilityIdentifier = "IMAPSecurityCell"
+    cell.textField.tag = 5
+    cell.textField.keyboardType = UIKeyboardType.numberPad
+    return cell
+  }()
+
+  lazy var smtpServerCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "SMTP Server", placeholder: MRConfig.sendServer ?? MRConfig.configuredSendServer, delegate: self)
+    cell.accessibilityIdentifier = "SMTPServerCell"
+    cell.textField.tag = 6
+    return cell
+  }()
+
+  lazy var smtpUserCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "SMTP User", placeholder: MRConfig.sendUser ?? MRConfig.configuredSendUser, delegate: self)
+    cell.accessibilityIdentifier = "SMTPUserCell"
+    cell.textField.tag = 7
+    return cell
+  }()
+
+  lazy var smtpPortCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "SMTP Port", placeholder: MRConfig.sendPort ?? MRConfig.configuredSendPort, delegate: self)
+    cell.accessibilityIdentifier = "SMTPPortCell"
+    cell.textField.tag = 8
+    return cell
+  }()
+
+  lazy var smtpPasswordCell: TextFieldCell = {
+    let cell = TextFieldCell(description: "SMTP Password", placeholder: "*************", delegate: self)
+    cell.accessibilityIdentifier = "SMTPPasswordCell"
+    cell.textField.tag = 9
+    return cell
+  }()
+
+  lazy var smtpSecurityCell: TextFieldCell = {
+    let text = "\(MRConfig.getSmtpSecurity())"
+    let cell = TextFieldCell(description: "SMTP Security", placeholder: text, delegate: self)
+    cell.accessibilityIdentifier = "SMTPSecurityCell"
+    cell.textField.tag = 10
+    cell.textField.keyboardType = UIKeyboardType.numberPad
+    return cell
+  }()
+
+  // this loginButton can be enabled and disabled
+  let loginButton: UIBarButtonItem = UIBarButtonItem(title: "Login", style: .done, target: self, action: #selector(loginButtonPressed))
+
+  private lazy var basicSectionCells: [UITableViewCell] = [emailCell, passwordCell]
+  private lazy var advancedSectionCells: [UITableViewCell] = [
+    imapServerCell,
+    imapUserCell,
+    imapPortCell,
+    imapSecurityCell,
+    smtpServerCell,
+    smtpUserCell,
+    smtpPortCell,
+    smtpPasswordCell,
+    smtpSecurityCell,
+  ]
+
+  private var advancedSectionShowing: Bool = false
+
+  init() {
+    super.init(style: .grouped)
+  }
+
+  required init?(coder _: NSCoder) {
+    fatalError("init(coder:) has not been implemented")
+  }
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    title = "Login to your server"
+    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(closeButtonPressed))
+    navigationItem.rightBarButtonItem = loginButton
+  }
+
+  override func viewDidAppear(_ animated: Bool) {
+    super.viewDidAppear(animated)
+    addProgressHudEventListener()
+    // loginButton.isEnabled = false
+    print(emailCell.getText())
+    print("account setup appeared")
+  }
+
+  override func viewDidDisappear(_: Bool) {
+    let nc = NotificationCenter.default
+    if let backupProgressObserver = self.backupProgressObserver {
+      nc.removeObserver(backupProgressObserver)
+    }
+    if let configureProgressObserver = self.configureProgressObserver {
+      nc.removeObserver(configureProgressObserver)
+    }
+    if let oauth2Observer = self.oauth2Observer {
+      nc.removeObserver(oauth2Observer)
+    }
+  }
+
+  // MARK: - Table view data source
+
+  override func numberOfSections(in _: UITableView) -> Int {
+    // #warning Incomplete implementation, return the number of sections
+    return 2
+  }
+
+  override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
+    // #warning Incomplete implementation, return the number of rows
+    if section == 0 {
+      return basicSectionCells.count
+    } else {
+      return advancedSectionShowing ? advancedSectionCells.count : 0
+    }
+  }
+
+  override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
+    if section == 1 {
+      return "Advanced"
+    } else {
+      return nil
+    }
+  }
+
+  override func tableView(_: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+    if section == 1 {
+      // Advanced Header
+      let advancedView = AdvancedSectionHeader()
+      advancedView.handleTap = toggleAdvancedSection
+      // set tapHandler
+      return advancedView
+
+    } else {
+      return nil
+    }
+  }
+
+  override func tableView(_: UITableView, heightForHeaderInSection _: Int) -> CGFloat {
+    return 36.0
+  }
+
+  override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
+    if section == 0 {
+      return "There are no Delta Chat servers, your data stays on your device!"
+    } else {
+      return "For known email providers additional settings are setup automatically. Sometimes IMAP needs to be enabled in the web frontend. Consult your email provider or friends for help"
+    }
+  }
+
+  override func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+    let section = indexPath.section
+    let row = indexPath.row
+
+    if section == 0 {
+      // basicSection
+      return basicSectionCells[row]
+    } else {
+      // advancedSection
+      return advancedSectionCells[row]
+    }
+  }
+
+  override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+    // handle tap on password -> show eventuall oAuthDialogue
+    if let cell = tableView.cellForRow(at: indexPath) as? TextFieldCell {
+      if cell.accessibilityIdentifier == "passwordCell" {
+        if let emailAdress = cell.getText() {
+          _ = showOAuthAlertIfNeeded(emailAddress: emailAdress, handleCancel: nil)
+        }
+      }
+    }
+  }
+
+  private func toggleAdvancedSection(button: UILabel) {
+    let willShow = !advancedSectionShowing
+
+    // extract indexPaths from advancedCells
+    let advancedIndexPaths: [IndexPath] = advancedSectionCells.indices.map { IndexPath(row: $0, section: 1) }
+
+    // advancedSectionCells.indices.map({indexPaths.append(IndexPath(row: $0, section: 1))}
+
+    // set flag before delete/insert operation, because cellForRowAt will be triggered and uses this flag
+    advancedSectionShowing = willShow
+
+    button.text = willShow ? "Hide" : "Show"
+
+    if willShow {
+      tableView.insertRows(at: advancedIndexPaths, with: .fade)
+    } else {
+      tableView.deleteRows(at: advancedIndexPaths, with: .fade)
+    }
+  }
+
+  @objc private func loginButtonPressed() {
+    guard let emailAddress = emailCell.getText() else {
+      return // handle case when either email or pw fields are empty
+    }
+
+    let oAuthStarted = showOAuthAlertIfNeeded(emailAddress: emailAddress, handleCancel: loginButtonPressed) // if canceled we will run this method again but this time oAuthStarted will be false
+
+    if oAuthStarted {
+      // the loginFlow will be handled by oAuth2
+      return
+    }
+
+    let password = passwordCell.getText() ?? "" // empty passwords are ok -> for oauth there is no password needed
+    login(emailAddress: emailAddress, password: password)
+  }
+
+  private func login(emailAddress: String, password: String, skipAdvanceSetup: Bool = false) {
+    MRConfig.addr = emailAddress
+    MRConfig.mailPw = password
+    if skipAdvanceSetup {
+      evaluluateAdvancedSetup() // this will set MRConfig related to advanced fields
+    }
+    dc_configure(mailboxPointer)
+    hudHandler.showBackupHud("Configuring account")
+  }
+
+  @objc func closeButtonPressed() {
+    dismiss(animated: true, completion: nil)
+  }
+
+  // returns true if needed
+  private func showOAuthAlertIfNeeded(emailAddress: String, handleCancel: (() -> Void)?) -> Bool {
+    if MRConfig.getAuthFlags() == 4 {
+      // user has previously denied oAuth2-setup
+      return false
+    }
+
+    guard let oAuth2UrlPointer = dc_get_oauth2_url(mailboxPointer, emailAddress, "chat.delta:/auth") else {
+      return false
+    }
+
+    let oAuth2Url = String(cString: oAuth2UrlPointer)
+
+    if let url = URL(string: oAuth2Url) {
+      let title = "Continue with simplified setup"
+      // swiftlint:disable all
+      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."
+
+      let oAuthAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
+      let confirm = UIAlertAction(title: "Confirm", style: .default, handler: {
+        [unowned self] _ in
+        let nc = NotificationCenter.default
+        self.oauth2Observer = nc.addObserver(self, selector: #selector(self.oauthLoginApproved), name: NSNotification.Name("oauthLoginApproved"), object: nil)
+        self.launchOAuthBrowserWindow(url: url)
+      })
+      let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: {
+        _ in
+        MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL))
+        handleCancel?()
+      })
+      oAuthAlertController.addAction(confirm)
+      oAuthAlertController.addAction(cancel)
+
+      present(oAuthAlertController, animated: true, completion: nil)
+      return true
+    } else {
+      return false
+    }
+  }
+
+  @objc func oauthLoginApproved(notification: Notification) {
+    guard let userInfo = notification.userInfo, let token = userInfo["token"] as? String, let emailAddress = emailCell.getText() else {
+      return
+    }
+    passwordCell.setText(text: token)
+    MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
+    login(emailAddress: emailAddress, password: token, skipAdvanceSetup: true)
+  }
+
+  private func launchOAuthBrowserWindow(url: URL) {
+    UIApplication.shared.open(url) // this opens safari as seperate app
+  }
+
+  private func addProgressHudEventListener() {
+    let nc = NotificationCenter.default
+    backupProgressObserver = nc.addObserver(
+      forName: dcNotificationBackupProgress,
+      object: nil,
+      queue: nil
+    ) {
+      notification in
+      if let ui = notification.userInfo {
+        if ui["error"] as! Bool {
+          self.hudHandler.setHudError(ui["errorMessage"] as? String)
+        } else if ui["done"] as! Bool {
+          self.hudHandler.setHudDone(callback: self.handleLoginSuccess)
+        } else {
+          self.hudHandler.setHudProgress(ui["progress"] as! Int)
+        }
+      }
+    }
+    configureProgressObserver = nc.addObserver(
+      forName: dcNotificationConfigureProgress,
+      object: nil,
+      queue: nil
+    ) {
+      notification in
+      if let ui = notification.userInfo {
+        if ui["error"] as! Bool {
+          self.hudHandler.setHudError(ui["errorMessage"] as? String)
+        } else if ui["done"] as! Bool {
+          self.hudHandler.setHudDone(callback: self.handleLoginSuccess)
+        } else {
+          self.hudHandler.setHudProgress(ui["progress"] as! Int)
+        }
+      }
+    }
+  }
+
+  private func evaluluateAdvancedSetup() {
+    for cell in advancedSectionCells {
+      if let textFieldCell = cell as? TextFieldCell {
+        switch cell.accessibilityIdentifier {
+        case "IMAPServerCell":
+          MRConfig.mailServer = textFieldCell.getText() ?? nil
+        case "IMAPUserCell":
+          MRConfig.mailUser = textFieldCell.getText() ?? nil
+        case "IMAPPortCell":
+          MRConfig.mailPort = textFieldCell.getText() ?? nil
+        case "IMAPSecurityCell":
+          let flag = 0
+          MRConfig.setImapSecurity(imapFlags: flag)
+        case "SMTPServerCell":
+          MRConfig.sendServer = textFieldCell.getText() ?? nil
+        case "SMTPSUserCell":
+          MRConfig.sendUser = textFieldCell.getText() ?? nil
+        case "SMTPPortCell":
+          MRConfig.sendPort = textFieldCell.getText() ?? nil
+        case "SMTPPasswordCell":
+          MRConfig.sendPw = textFieldCell.getText() ?? nil
+        case "SMTPSecurityCell":
+          let flag = 0
+          MRConfig.setSmtpSecurity(smptpFlags: flag)
+        default:
+          logger.info("unknown identifier", cell.accessibilityIdentifier ?? "")
+        }
+      }
+    }
+  }
+
+  private func handleLoginSuccess() {
+    // used when login hud successfully went trough
+    dismiss(animated: true, completion: nil)
+  }
 }
 
 extension AccountSetupController: UITextFieldDelegate {
-	func textFieldShouldReturn(_ textField: UITextField) -> Bool {
-		let currentTag = textField.tag
-
-		if textField.accessibilityIdentifier == "emailTextField" && showOAuthAlertIfNeeded(emailAddress: textField.text ?? "", handleCancel: {
-				// special case: email field should check for potential oAuth
-
-				// this will activate passwordTextField if oAuth-Dialogue was canceled
-				self.passwordCell.textField.becomeFirstResponder()
-		}) {
-			// all the action is defined in if condition
-		} else {
-			if let nextField = tableView.viewWithTag(currentTag + 1) as? UITextField {
-				if nextField.tag > 1 && !advancedSectionShowing {
-					// gets here when trying to activate a collapsed cell
-					return false
-				}
-				nextField.becomeFirstResponder()
-			}
-		}
-
-		return false
-	}
+  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+    let currentTag = textField.tag
+
+    if textField.accessibilityIdentifier == "emailTextField", showOAuthAlertIfNeeded(emailAddress: textField.text ?? "", handleCancel: {
+      // special case: email field should check for potential oAuth
+
+      // this will activate passwordTextField if oAuth-Dialogue was canceled
+      passwordCell.textField.becomeFirstResponder()
+    }) {
+      // all the action is defined in if condition
+    } else {
+      if let nextField = tableView.viewWithTag(currentTag + 1) as? UITextField {
+        if nextField.tag > 1, !advancedSectionShowing {
+          // gets here when trying to activate a collapsed cell
+          return false
+        }
+        nextField.becomeFirstResponder()
+      }
+    }
+
+    return false
+  }
 }
 
 class AdvancedSectionHeader: UIView {
-
-	var handleTap:((UILabel) -> ())?
-
-	private var label:UILabel = {
-		let label = UILabel()
-		label.text = "ADVANCED"
-		label.font = UIFont.systemFont(ofSize: 15)
-		label.textColor = UIColor.darkGray
-		return label
-	}()
-
-	/*
-	why UILabel, why no UIButton? For unknown reasons UIButton's target function was not triggered when one of the textfields in the tableview was active -> used label as workaround
-	*/
-	private lazy var toggleButton:UILabel = {
-		let label = UILabel()
-		label.text = "Show"
-		label.font = UIFont.systemFont(ofSize: 15, weight: .medium)
-		label.textColor = UIColor.systemBlue
-		return label
-	}()
+  var handleTap: ((UILabel) -> Void)?
+
+  private var label: UILabel = {
+    let label = UILabel()
+    label.text = "ADVANCED"
+    label.font = UIFont.systemFont(ofSize: 15)
+    label.textColor = UIColor.darkGray
+    return label
+  }()
+
+  /*
+   why UILabel, why no UIButton? For unknown reasons UIButton's target function was not triggered when one of the textfields in the tableview was active -> used label as workaround
+   */
+  private lazy var toggleButton: UILabel = {
+    let label = UILabel()
+    label.text = "Show"
+    label.font = UIFont.systemFont(ofSize: 15, weight: .medium)
+    label.textColor = UIColor.systemBlue
+    return label
+  }()
 
 //
-//	private var toggleButton:UIButton = {
-//		let button = UIButton(type: .system)
-//		button.setTitle("Show", for: .normal)
-//		button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside )
-//		//button.target(forAction: #selector(buttonTapped(_:)), withSender: self)
-//		return button
-//	}()
-
-	init() {
-		super.init(frame: .zero)    // will be constraint from tableViewDelegate
-		setupSubviews()
-		let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped)) // use this if the whole header is supposed to be clickable
-		self.addGestureRecognizer(tap)
-
-	}
-
-	required init?(coder aDecoder: NSCoder) {
-		fatalError("init(coder:) has not been implemented")
-	}
-
-	func setupSubviews() {
-		self.addSubview(label)
-		label.translatesAutoresizingMaskIntoConstraints = false
-		label.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant:  15).isActive = true
-		label.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
-		self.addSubview(toggleButton)
-		toggleButton.translatesAutoresizingMaskIntoConstraints = false
-
-		toggleButton.leadingAnchor.constraint(equalTo: self.trailingAnchor, constant: -60).isActive = true	// since button will change title it should be left aligned
-		toggleButton.centerYAnchor.constraint(equalTo: label.centerYAnchor, constant: 0).isActive = true
-	}
-
-	@objc func buttonTapped(_ button: UIButton) {
-		//handleTap?(button)
-	}
-
-	@objc func viewTapped() {
-		handleTap?(self.toggleButton)
-	}
-}
-
-
-
-
-
-
-/*
-class InputTableViewCell: UITableViewCell {
-lazy var inputField: UITextField = {
-let textField = UITextField()
-return textField
-}()
-
-init() {
-super.init(style: .default, reuseIdentifier: nil)
-setupView()
-}
-
-required init?(coder aDecoder: NSCoder) {
-fatalError("init(coder:) has not been implemented")
+  //	private var toggleButton:UIButton = {
+  //		let button = UIButton(type: .system)
+  //		button.setTitle("Show", for: .normal)
+  //		button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside )
+  //		//button.target(forAction: #selector(buttonTapped(_:)), withSender: self)
+  //		return button
+  //	}()
+
+  init() {
+    super.init(frame: .zero) // will be constraint from tableViewDelegate
+    setupSubviews()
+    let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped)) // use this if the whole header is supposed to be clickable
+    addGestureRecognizer(tap)
+  }
+
+  required init?(coder _: NSCoder) {
+    fatalError("init(coder:) has not been implemented")
+  }
+
+  func setupSubviews() {
+    addSubview(label)
+    label.translatesAutoresizingMaskIntoConstraints = false
+    label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15).isActive = true
+    label.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
+    addSubview(toggleButton)
+    toggleButton.translatesAutoresizingMaskIntoConstraints = false
+
+    toggleButton.leadingAnchor.constraint(equalTo: trailingAnchor, constant: -60).isActive = true // since button will change title it should be left aligned
+    toggleButton.centerYAnchor.constraint(equalTo: label.centerYAnchor, constant: 0).isActive = true
+  }
+
+  @objc func buttonTapped(_: UIButton) {
+    // handleTap?(button)
+  }
+
+  @objc func viewTapped() {
+    handleTap?(toggleButton)
+  }
 }
 
-private func setupView() {
-contentView.addSubview(inputField)
-inputField.translatesAutoresizingMaskIntoConstraints = false
-inputField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0).isActive = true
-inputField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
-inputField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
-inputField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 100).isActive = true
-inputField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
-}
-public func getText() -> String? {
-return inputField.text
-}
-}
-
-class PasswordInputCell: UITableViewCell {
-lazy var inputField: UITextField = {
-let textField = UITextField()
-textField.isSecureTextEntry = true
-return textField
-}()
-
-// TODO: to add Eye-icon -> uncomment -> add to inputField.rightView
 /*
-lazy var makeVisibleIcon: UIImageView = {
-let view = UIImageView(image: )
-return view
-}()
-*/
-init() {
-super.init(style: .default, reuseIdentifier: nil)
-setupView()
-}
-
-required init?(coder aDecoder: NSCoder) {
-fatalError("init(coder:) has not been implemented")
-}
-
-private func setupView() {
-contentView.addSubview(inputField)
-inputField.translatesAutoresizingMaskIntoConstraints = false
-inputField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0).isActive = true
-inputField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
-inputField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
-inputField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 100).isActive = true
-inputField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
-}
-
-public func getText() -> String? {
-return inputField.text
-}
-}
-
-*/
+ class InputTableViewCell: UITableViewCell {
+ lazy var inputField: UITextField = {
+ let textField = UITextField()
+ return textField
+ }()
+
+ init() {
+ super.init(style: .default, reuseIdentifier: nil)
+ setupView()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setupView() {
+ contentView.addSubview(inputField)
+ inputField.translatesAutoresizingMaskIntoConstraints = false
+ inputField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0).isActive = true
+ inputField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
+ inputField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
+ inputField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 100).isActive = true
+ inputField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
+ }
+ public func getText() -> String? {
+ return inputField.text
+ }
+ }
+
+ class PasswordInputCell: UITableViewCell {
+ lazy var inputField: UITextField = {
+ let textField = UITextField()
+ textField.isSecureTextEntry = true
+ return textField
+ }()
+
+ // TODO: to add Eye-icon -> uncomment -> add to inputField.rightView
+ /*
+  lazy var makeVisibleIcon: UIImageView = {
+  let view = UIImageView(image: )
+  return view
+  }()
+  */
+ init() {
+ super.init(style: .default, reuseIdentifier: nil)
+ setupView()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setupView() {
+ contentView.addSubview(inputField)
+ inputField.translatesAutoresizingMaskIntoConstraints = false
+ inputField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0).isActive = true
+ inputField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
+ inputField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
+ inputField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 100).isActive = true
+ inputField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
+ }
+
+ public func getText() -> String? {
+ return inputField.text
+ }
+ }
+
+ */

+ 10 - 15
deltachat-ios/AppCoordinator.swift

@@ -14,16 +14,15 @@ protocol Coordinator {
 
 class AppCoordinator: Coordinator {
   let baseController = BaseController()
-  
+
   private var appTabBarController: AppTabBarController = AppTabBarController()
 
   func setupViewControllers(window: UIWindow) {
     window.rootViewController = appTabBarController
     window.makeKeyAndVisible()
-    
   }
-  
-	func presentAccountSetup(animated: Bool) {
+
+  func presentAccountSetup(animated: Bool) {
     let accountSetupController = AccountSetupController()
     let accountSetupNavigationController = UINavigationController(rootViewController: accountSetupController)
     appTabBarController.present(accountSetupNavigationController, animated: animated, completion: nil)
@@ -34,16 +33,12 @@ class AppCoordinator: Coordinator {
     let chatNavigationController = UINavigationController(rootViewController: chatListController)
     baseController.present(chatNavigationController, animated: false, completion: nil)
   }
-  
+
   /*
-  func setupAccountSetup() {
-    let accountSetupController = AccountSetupController()
-    let accountSetupNavigationController = UINavigationController(rootViewController: accountSetupController)
-    baseController.present(accountSetupNavigationController, animated: false, completion: nil)
-  }
-  */
-    
-    
-    
-    
+   func setupAccountSetup() {
+     let accountSetupController = AccountSetupController()
+     let accountSetupNavigationController = UINavigationController(rootViewController: accountSetupController)
+     baseController.present(accountSetupNavigationController, animated: false, completion: nil)
+   }
+   */
 }

+ 296 - 297
deltachat-ios/AppDelegate.swift

@@ -17,305 +17,304 @@ var mailboxPointer: UnsafeMutablePointer<dc_context_t>!
 let logger = SwiftyBeaver.self
 
 enum ApplicationState {
-	case stopped
-	case running
-	case background
-	case backgroundFetch
+  case stopped
+  case running
+  case background
+  case backgroundFetch
 }
 
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
-	static let appCoordinator = AppCoordinator()
-	static var progress: Float = 0
-	static var lastErrorDuringConfig: String?
-	var backgroundTask: UIBackgroundTaskIdentifier = .invalid
-
-	var reachability = Reachability()!
-	var window: UIWindow?
-
-	var state = ApplicationState.stopped
-
-	private func getCoreInfo() -> [[String]] {
-		if let cInfo = dc_get_info(mailboxPointer) {
-			let info = String(cString: cInfo)
-			logger.info(info)
-			return info.components(separatedBy: "\n").map { val in
-				val.components(separatedBy: "=")
-			}
-		}
-
-		return []
-	}
-
-	func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
-		// gets here when app returns from oAuth2-Setup process - the url contains the provided token
-
-		if let params = url.queryParameters, let token = params["code"] {
-//			MRConfig.mailPw = authorizationCode
-//			MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
-//			dc_configure(mailboxPointer)
-			NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token":token])
-		}
-
-
-		return true
-	}
-
-	func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
-		DBDebugToolkit.setup()
-		DBDebugToolkit.setupCrashReporting()
-
-		let console = ConsoleDestination()
-		logger.addDestination(console)
-
-		logger.info("launching")
-
-		// Override point for customization after application launch.
-
-		window = UIWindow(frame: UIScreen.main.bounds)
-		guard let window = window else {
-			fatalError("window was nil in app delegate")
-		}
-		// setup deltachat core context
-		//       - second param remains nil (user data for more than one mailbox)
-		mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
-		guard mailboxPointer != nil else {
-			fatalError("Error: dc_context_new returned nil")
-		}
-		open()
-		let isConfigured = dc_is_configured(mailboxPointer) != 0
-		AppDelegate.appCoordinator.setupViewControllers(window: window)
-		UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
-		start()
-		registerForPushNotifications()
-		if !isConfigured {
-			AppDelegate.appCoordinator.presentAccountSetup(animated: false)
-		}
-		return true
-	}
-
-	func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
-		logger.info("---- background-fetch ----")
-
-		start {
-			// TODO: actually set the right value depending on if we found sth
-			completionHandler(.newData)
-		}
-	}
-
-	func applicationWillEnterForeground(_: UIApplication) {
-		logger.info("---- foreground ----")
-		start()
-	}
-
-	func applicationDidEnterBackground(_: UIApplication) {
-		logger.info("---- background ----")
-
-		reachability.stopNotifier()
-		NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
-
-		maybeStop()
-	}
-
-	func maybeStop() {
-		DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-			let app = UIApplication.shared
-			logger.info("state: \(app.applicationState) time remaining \(app.backgroundTimeRemaining)")
-
-			if app.applicationState != .background {
-				// only need to do sth in the background
-				return
-			} else if app.backgroundTimeRemaining < 10 {
-				self.stop()
-			} else {
-				self.maybeStop()
-			}
-		}
-	}
-
-	func applicationWillTerminate(_: UIApplication) {
-		logger.info("---- terminate ----")
-		close()
-
-		reachability.stopNotifier()
-		NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
-	}
-
-	func dbfile() -> String {
-		let paths = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
-		let documentsPath = paths[0]
-
-		return documentsPath + "/messenger.db"
-	}
-
-	func open() {
-		logger.info("open: \(dbfile())")
-
-		_ = dc_open(mailboxPointer, dbfile(), nil)
-	}
-
-	func stop() {
-		state = .background
-
-		dc_interrupt_imap_idle(mailboxPointer)
-		dc_interrupt_smtp_idle(mailboxPointer)
-		dc_interrupt_mvbox_idle(mailboxPointer)
-		dc_interrupt_sentbox_idle(mailboxPointer)
-	}
-
-	func close() {
-		state = .stopped
-
-		dc_close(mailboxPointer)
-		mailboxPointer = nil
-	}
-
-	func start(_ completion: (() -> Void)? = nil) {
-		logger.info("---- start ----")
-
-		if state == .running {
-			return
-		}
-
-		state = .running
-
-		DispatchQueue.global(qos: .background).async {
-			self.registerBackgroundTask()
-			while self.state == .running {
-				dc_perform_imap_jobs(mailboxPointer)
-				dc_perform_imap_fetch(mailboxPointer)
-				dc_perform_imap_idle(mailboxPointer)
-			}
-			if self.backgroundTask != .invalid {
-				completion?()
-				self.endBackgroundTask()
-			}
-		}
-
-		DispatchQueue.global(qos: .utility).async {
-			self.registerBackgroundTask()
-			while self.state == .running {
-				dc_perform_smtp_jobs(mailboxPointer)
-				dc_perform_smtp_idle(mailboxPointer)
-			}
-			if self.backgroundTask != .invalid {
-				self.endBackgroundTask()
-			}
-		}
-
-		if MRConfig.sentboxWatch {
-			DispatchQueue.global(qos: .background).async {
-				while self.state == .running {
-					dc_perform_sentbox_fetch(mailboxPointer)
-					dc_perform_sentbox_idle(mailboxPointer)
-				}
-			}
-		}
-
-		if MRConfig.mvboxWatch {
-			DispatchQueue.global(qos: .background).async {
-				while self.state == .running {
-					dc_perform_mvbox_fetch(mailboxPointer)
-					dc_perform_mvbox_idle(mailboxPointer)
-				}
-			}
-		}
-
-		NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
-		do {
-			try reachability.startNotifier()
-		} catch {
-			logger.info("could not start reachability notifier")
-		}
-
-		let info: [DBCustomVariable] = getCoreInfo().map { kv in
-			let value = kv.count > 1 ? kv[1] : ""
-			return DBCustomVariable(name: kv[0], value: value)
-		}
-
-		DBDebugToolkit.add(info)
-	}
-
-	@objc func reachabilityChanged(note: Notification) {
-		let reachability = note.object as! Reachability
-
-		switch reachability.connection {
-		case .wifi, .cellular:
-			logger.info("network: reachable", reachability.connection.description)
-			dc_maybe_network(mailboxPointer)
-
-			let nc = NotificationCenter.default
-			DispatchQueue.main.async {
-				nc.post(name: dcNotificationStateChanged,
-						object: nil,
-						userInfo: ["state": "online"])
-			}
-		case .none:
-			logger.info("network: not reachable")
-			let nc = NotificationCenter.default
-			DispatchQueue.main.async {
-				nc.post(name: dcNotificationStateChanged,
-						object: nil,
-						userInfo: ["state": "offline"])
-			}
-		}
-	}
-
-	// MARK: - BackgroundTask
-
-	func registerBackgroundTask() {
-		logger.info("background task registered")
-		backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
-			self?.endBackgroundTask()
-		}
-		assert(backgroundTask != .invalid)
-	}
-
-	func endBackgroundTask() {
-		logger.info("background task ended")
-		UIApplication.shared.endBackgroundTask(backgroundTask)
-		backgroundTask = .invalid
-	}
-
-	// MARK: - PushNotifications
-
-	func registerForPushNotifications() {
-		UNUserNotificationCenter.current().delegate = self
-
-		UNUserNotificationCenter.current()
-			.requestAuthorization(options: [.alert, .sound, .badge]) {
-				granted, _ in
-				logger.info("permission granted: \(granted)")
-				guard granted else { return }
-				self.getNotificationSettings()
-		}
-	}
-
-	func getNotificationSettings() {
-		UNUserNotificationCenter.current().getNotificationSettings { settings in
-			logger.info("Notification settings: \(settings)")
-		}
-	}
-
-	func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
-		logger.info("forground notification")
-		completionHandler([.alert, .sound])
-	}
-
-	func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
-		if response.notification.request.identifier == Constants.notificationIdentifier {
-			logger.info("handling notifications")
-			let userInfo = response.notification.request.content.userInfo
-			let nc = NotificationCenter.default
-			DispatchQueue.main.async {
-				nc.post(
-					name: dcNotificationViewChat,
-					object: nil,
-					userInfo: userInfo
-				)
-			}
-		}
-
-		completionHandler()
-	}
+  static let appCoordinator = AppCoordinator()
+  static var progress: Float = 0
+  static var lastErrorDuringConfig: String?
+  var backgroundTask: UIBackgroundTaskIdentifier = .invalid
+
+  var reachability = Reachability()!
+  var window: UIWindow?
+
+  var state = ApplicationState.stopped
+
+  private func getCoreInfo() -> [[String]] {
+    if let cInfo = dc_get_info(mailboxPointer) {
+      let info = String(cString: cInfo)
+      logger.info(info)
+      return info.components(separatedBy: "\n").map { val in
+        val.components(separatedBy: "=")
+      }
+    }
+
+    return []
+  }
+
+  func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
+    // gets here when app returns from oAuth2-Setup process - the url contains the provided token
+
+    if let params = url.queryParameters, let token = params["code"] {
+      //			MRConfig.mailPw = authorizationCode
+      //			MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
+      //			dc_configure(mailboxPointer)
+      NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token": token])
+    }
+
+    return true
+  }
+
+  func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+    DBDebugToolkit.setup()
+    DBDebugToolkit.setupCrashReporting()
+
+    let console = ConsoleDestination()
+    logger.addDestination(console)
+
+    logger.info("launching")
+
+    // Override point for customization after application launch.
+
+    window = UIWindow(frame: UIScreen.main.bounds)
+    guard let window = window else {
+      fatalError("window was nil in app delegate")
+    }
+    // setup deltachat core context
+    //       - second param remains nil (user data for more than one mailbox)
+    mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
+    guard mailboxPointer != nil else {
+      fatalError("Error: dc_context_new returned nil")
+    }
+    open()
+    let isConfigured = dc_is_configured(mailboxPointer) != 0
+    AppDelegate.appCoordinator.setupViewControllers(window: window)
+    UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
+    start()
+    registerForPushNotifications()
+    if !isConfigured {
+      AppDelegate.appCoordinator.presentAccountSetup(animated: false)
+    }
+    return true
+  }
+
+  func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+    logger.info("---- background-fetch ----")
+
+    start {
+      // TODO: actually set the right value depending on if we found sth
+      completionHandler(.newData)
+    }
+  }
+
+  func applicationWillEnterForeground(_: UIApplication) {
+    logger.info("---- foreground ----")
+    start()
+  }
+
+  func applicationDidEnterBackground(_: UIApplication) {
+    logger.info("---- background ----")
+
+    reachability.stopNotifier()
+    NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
+
+    maybeStop()
+  }
+
+  func maybeStop() {
+    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+      let app = UIApplication.shared
+      logger.info("state: \(app.applicationState) time remaining \(app.backgroundTimeRemaining)")
+
+      if app.applicationState != .background {
+        // only need to do sth in the background
+        return
+      } else if app.backgroundTimeRemaining < 10 {
+        self.stop()
+      } else {
+        self.maybeStop()
+      }
+    }
+  }
+
+  func applicationWillTerminate(_: UIApplication) {
+    logger.info("---- terminate ----")
+    close()
+
+    reachability.stopNotifier()
+    NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
+  }
+
+  func dbfile() -> String {
+    let paths = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
+    let documentsPath = paths[0]
+
+    return documentsPath + "/messenger.db"
+  }
+
+  func open() {
+    logger.info("open: \(dbfile())")
+
+    _ = dc_open(mailboxPointer, dbfile(), nil)
+  }
+
+  func stop() {
+    state = .background
+
+    dc_interrupt_imap_idle(mailboxPointer)
+    dc_interrupt_smtp_idle(mailboxPointer)
+    dc_interrupt_mvbox_idle(mailboxPointer)
+    dc_interrupt_sentbox_idle(mailboxPointer)
+  }
+
+  func close() {
+    state = .stopped
+
+    dc_close(mailboxPointer)
+    mailboxPointer = nil
+  }
+
+  func start(_ completion: (() -> Void)? = nil) {
+    logger.info("---- start ----")
+
+    if state == .running {
+      return
+    }
+
+    state = .running
+
+    DispatchQueue.global(qos: .background).async {
+      self.registerBackgroundTask()
+      while self.state == .running {
+        dc_perform_imap_jobs(mailboxPointer)
+        dc_perform_imap_fetch(mailboxPointer)
+        dc_perform_imap_idle(mailboxPointer)
+      }
+      if self.backgroundTask != .invalid {
+        completion?()
+        self.endBackgroundTask()
+      }
+    }
+
+    DispatchQueue.global(qos: .utility).async {
+      self.registerBackgroundTask()
+      while self.state == .running {
+        dc_perform_smtp_jobs(mailboxPointer)
+        dc_perform_smtp_idle(mailboxPointer)
+      }
+      if self.backgroundTask != .invalid {
+        self.endBackgroundTask()
+      }
+    }
+
+    if MRConfig.sentboxWatch {
+      DispatchQueue.global(qos: .background).async {
+        while self.state == .running {
+          dc_perform_sentbox_fetch(mailboxPointer)
+          dc_perform_sentbox_idle(mailboxPointer)
+        }
+      }
+    }
+
+    if MRConfig.mvboxWatch {
+      DispatchQueue.global(qos: .background).async {
+        while self.state == .running {
+          dc_perform_mvbox_fetch(mailboxPointer)
+          dc_perform_mvbox_idle(mailboxPointer)
+        }
+      }
+    }
+
+    NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
+    do {
+      try reachability.startNotifier()
+    } catch {
+      logger.info("could not start reachability notifier")
+    }
+
+    let info: [DBCustomVariable] = getCoreInfo().map { kv in
+      let value = kv.count > 1 ? kv[1] : ""
+      return DBCustomVariable(name: kv[0], value: value)
+    }
+
+    DBDebugToolkit.add(info)
+  }
+
+  @objc func reachabilityChanged(note: Notification) {
+    let reachability = note.object as! Reachability
+
+    switch reachability.connection {
+    case .wifi, .cellular:
+      logger.info("network: reachable", reachability.connection.description)
+      dc_maybe_network(mailboxPointer)
+
+      let nc = NotificationCenter.default
+      DispatchQueue.main.async {
+        nc.post(name: dcNotificationStateChanged,
+                object: nil,
+                userInfo: ["state": "online"])
+      }
+    case .none:
+      logger.info("network: not reachable")
+      let nc = NotificationCenter.default
+      DispatchQueue.main.async {
+        nc.post(name: dcNotificationStateChanged,
+                object: nil,
+                userInfo: ["state": "offline"])
+      }
+    }
+  }
+
+  // MARK: - BackgroundTask
+
+  func registerBackgroundTask() {
+    logger.info("background task registered")
+    backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
+      self?.endBackgroundTask()
+    }
+    assert(backgroundTask != .invalid)
+  }
+
+  func endBackgroundTask() {
+    logger.info("background task ended")
+    UIApplication.shared.endBackgroundTask(backgroundTask)
+    backgroundTask = .invalid
+  }
+
+  // MARK: - PushNotifications
+
+  func registerForPushNotifications() {
+    UNUserNotificationCenter.current().delegate = self
+
+    UNUserNotificationCenter.current()
+      .requestAuthorization(options: [.alert, .sound, .badge]) {
+        granted, _ in
+        logger.info("permission granted: \(granted)")
+        guard granted else { return }
+        self.getNotificationSettings()
+      }
+  }
+
+  func getNotificationSettings() {
+    UNUserNotificationCenter.current().getNotificationSettings { settings in
+      logger.info("Notification settings: \(settings)")
+    }
+  }
+
+  func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+    logger.info("forground notification")
+    completionHandler([.alert, .sound])
+  }
+
+  func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
+    if response.notification.request.identifier == Constants.notificationIdentifier {
+      logger.info("handling notifications")
+      let userInfo = response.notification.request.content.userInfo
+      let nc = NotificationCenter.default
+      DispatchQueue.main.async {
+        nc.post(
+          name: dcNotificationViewChat,
+          object: nil,
+          userInfo: userInfo
+        )
+      }
+    }
+
+    completionHandler()
+  }
 }

+ 1 - 1
deltachat-ios/ChatViewController.swift

@@ -95,7 +95,7 @@ class ChatViewController: MessagesViewController {
       ids = Utils.copyAndFreeArrayWithLen(inputArray: cMessageIds, len: count)
     }
 
-    let markIds: [UInt32] = ids.map { return UInt32($0) }
+    let markIds: [UInt32] = ids.map { UInt32($0) }
     dc_markseen_msgs(mailboxPointer, UnsafePointer(markIds), Int32(ids.count))
 
     return ids.map {

+ 45 - 48
deltachat-ios/Extensions/Extensions.swift

@@ -9,72 +9,69 @@
 import UIKit
 
 extension String {
-
-	func containsCharacters() -> Bool {
-		return !self.trimmingCharacters(in: [" "]).isEmpty
-	}
+  func containsCharacters() -> Bool {
+    return !trimmingCharacters(in: [" "]).isEmpty
+  }
 }
 
 extension UIColor {
-
-	static var systemBlue: UIColor {
-		return UIButton(type: .system).tintColor
-	}
-
+  static var systemBlue: UIColor {
+    return UIButton(type: .system).tintColor
+  }
 }
 
 extension URL {
-	public var queryParameters: [String: String]? {
-		guard
-			let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
-			let queryItems = components.queryItems else { return nil }
-		return queryItems.reduce(into: [String: String]()) { (result, item) in
-			result[item.name] = item.value
-		}
-	}
+  public var queryParameters: [String: String]? {
+    guard
+      let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
+      let queryItems = components.queryItems else { return nil }
+    return queryItems.reduce(into: [String: String]()) { result, item in
+      result[item.name] = item.value
+    }
+  }
 }
 
 extension Dictionary {
-	func percentEscaped() -> String {
-		return map { (key, value) in
-			let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
-			let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
-			return escapedKey + "=" + escapedValue
-			}
-			.joined(separator: "&")
-	}
+  func percentEscaped() -> String {
+    return map { key, value in
+      let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
+      let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
+      return escapedKey + "=" + escapedValue
+    }
+    .joined(separator: "&")
+  }
 }
 
 extension CharacterSet {
-	static let urlQueryValueAllowed: CharacterSet = {
-		let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
-		let subDelimitersToEncode = "!$&'()*+,;="
-
-		var allowed = CharacterSet.urlQueryAllowed
-		allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
-		return allowed
-	}()
+  static let urlQueryValueAllowed: CharacterSet = {
+    let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
+    let subDelimitersToEncode = "!$&'()*+,;="
+
+    var allowed = CharacterSet.urlQueryAllowed
+    allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
+    return allowed
+  }()
 }
 
 extension URLSession {
-	func synchronousDataTask(request: URLRequest) -> (Data?, URLResponse?, Error?) {
-		var data: Data?
-		var response: URLResponse?
-		var error: Error?
+  func synchronousDataTask(request: URLRequest) -> (Data?, URLResponse?, Error?) {
+    var data: Data?
+    var response: URLResponse?
+    var error: Error?
 
-		let semaphore = DispatchSemaphore(value: 0)
+    let semaphore = DispatchSemaphore(value: 0)
 
-		let task = self.dataTask(with: request) {
-			data = $0
-			response = $1
-			error = $2
+    let task = dataTask(with: request) {
+      data = $0
+      response = $1
+      error = $2
 
-			semaphore.signal()
-		}
-		task.resume()
+      semaphore.signal()
+    }
+    task.resume()
 
-		_ = semaphore.wait(timeout: .distantFuture)
+    _ = semaphore.wait(timeout: .distantFuture)
 
-		return (data, response, error)
-	}
+    return (data, response, error)
+  }
 }

+ 51 - 51
deltachat-ios/HudHandler.swift

@@ -6,69 +6,69 @@
 //  Copyright © 2019 Jonas Reinsch. All rights reserved.
 //
 
-import UIKit
 import JGProgressHUD
+import UIKit
 
 class HudHandler {
-    var backupHud: JGProgressHUD?
-    unowned var view:UIView
+  var backupHud: JGProgressHUD?
+  unowned var view: UIView
 
-    init(parentView: UIView) {
-        self.view = parentView
-    }
+  init(parentView: UIView) {
+    view = parentView
+  }
 
-    func setHudProgress(_ progress: Int) {
-        if let hud = self.backupHud {
-            hud.progress = Float(progress) / 1000.0
-            hud.detailTextLabel.text = "\(progress / 10)% Complete"
-        }
+  func setHudProgress(_ progress: Int) {
+    if let hud = self.backupHud {
+      hud.progress = Float(progress) / 1000.0
+      hud.detailTextLabel.text = "\(progress / 10)% Complete"
     }
+  }
 
-    func showBackupHud(_ text: String) {
-        DispatchQueue.main.async {
-            let hud = JGProgressHUD(style: .dark)
-            hud.vibrancyEnabled = true
-            hud.indicatorView = JGProgressHUDPieIndicatorView()
-            hud.detailTextLabel.text = "0% Complete"
-            hud.textLabel.text = text
-            hud.show(in: self.view)
-            self.backupHud = hud
-        }
+  func showBackupHud(_ text: String) {
+    DispatchQueue.main.async {
+      let hud = JGProgressHUD(style: .dark)
+      hud.vibrancyEnabled = true
+      hud.indicatorView = JGProgressHUDPieIndicatorView()
+      hud.detailTextLabel.text = "0% Complete"
+      hud.textLabel.text = text
+      hud.show(in: self.view)
+      self.backupHud = hud
     }
+  }
 
-    func setHudError(_ message: String?) {
-        if let hud = self.backupHud {
-            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
-                UIView.animate(
-                    withDuration: 0.1, animations: {
-                        hud.textLabel.text = message ?? "Error"
-                        hud.detailTextLabel.text = nil
-                        hud.indicatorView = JGProgressHUDErrorIndicatorView()
-                }
-                )
-                hud.dismiss(afterDelay: 5.0)
-            }
-        }
+  func setHudError(_ message: String?) {
+    if let hud = self.backupHud {
+      DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
+        UIView.animate(
+          withDuration: 0.1, animations: {
+            hud.textLabel.text = message ?? "Error"
+            hud.detailTextLabel.text = nil
+            hud.indicatorView = JGProgressHUDErrorIndicatorView()
+          }
+        )
+        hud.dismiss(afterDelay: 5.0)
+      }
     }
+  }
 
-    func setHudDone(callback: (() -> Void)?) {
-		let delay = 1.0
-
-		if let hud = self.backupHud {
-            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
-                UIView.animate(
-                    withDuration: 0.1, animations: {
-                        hud.textLabel.text = "Success"
-                        hud.detailTextLabel.text = nil
-                        hud.indicatorView = JGProgressHUDSuccessIndicatorView()
+  func setHudDone(callback: (() -> Void)?) {
+    let delay = 1.0
 
-                })
-            }
+    if let hud = self.backupHud {
+      DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
+        UIView.animate(
+          withDuration: 0.1, animations: {
+            hud.textLabel.text = "Success"
+            hud.detailTextLabel.text = nil
+            hud.indicatorView = JGProgressHUDSuccessIndicatorView()
+          }
+        )
+      }
 
-			DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
-				callback?()
-				hud.dismiss()
-			})
-        }
+      DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
+        callback?()
+        hud.dismiss()
+      }
     }
+  }
 }

+ 103 - 106
deltachat-ios/TextFieldCell.swift

@@ -1,6 +1,6 @@
 //
 //  TextFieldCell.swift
-//  deltachat-ios< 
+//  deltachat-ios<
 //
 //  Created by Friedel Ziegelmayer on 27.12.18.
 //  Copyright © 2018 Jonas Reinsch. All rights reserved.
@@ -9,113 +9,110 @@
 import UIKit
 
 class TextFieldCell: UITableViewCell {
-
-    private let placeholder:String
-
-    lazy var textField: UITextField = {
-        let textField = UITextField()
-        textField.textAlignment = .right
-       // textField.enablesReturnKeyAutomatically = true
-        textField.placeholder = self.placeholder
-        return textField
-    }()
-
-	init(description: String, placeholder: String, delegate: UITextFieldDelegate? = nil) {
-        self.placeholder = placeholder
-        super.init(style: .value1, reuseIdentifier: nil)
-        textLabel?.text = "\(description):"
-
-        // see: https://stackoverflow.com/a/35903650
-        // this makes the textField respect the trailing margin of
-        // the table view cell
-        selectionStyle = .none
-        setupViews()
-		textField.delegate = delegate
-    }
-
-    required init?(coder _: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    private func setupViews() {
-        contentView.addSubview(textField)
-        textField.translatesAutoresizingMaskIntoConstraints = false
-        let margins = contentView.layoutMarginsGuide
-        let trailing = margins.trailingAnchor
-        textField.trailingAnchor.constraint(equalTo: trailing).isActive = true
-        textField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
-        if let label = self.textLabel {
-            textField.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: 20).isActive = true // this will prevent the textfield from growing over the textLabel while typing
-        } else {
-            textField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
-        }
-    }
-
-    override func setSelected(_ selected: Bool, animated _: Bool) {
-        if selected {
-            textField.becomeFirstResponder()
-        }
+  private let placeholder: String
+
+  lazy var textField: UITextField = {
+    let textField = UITextField()
+    textField.textAlignment = .right
+    // textField.enablesReturnKeyAutomatically = true
+    textField.placeholder = self.placeholder
+    return textField
+  }()
+
+  init(description: String, placeholder: String, delegate: UITextFieldDelegate? = nil) {
+    self.placeholder = placeholder
+    super.init(style: .value1, reuseIdentifier: nil)
+    textLabel?.text = "\(description):"
+
+    // see: https://stackoverflow.com/a/35903650
+    // this makes the textField respect the trailing margin of
+    // the table view cell
+    selectionStyle = .none
+    setupViews()
+    textField.delegate = delegate
+  }
+
+  required init?(coder _: NSCoder) {
+    fatalError("init(coder:) has not been implemented")
+  }
+
+  private func setupViews() {
+    contentView.addSubview(textField)
+    textField.translatesAutoresizingMaskIntoConstraints = false
+    let margins = contentView.layoutMarginsGuide
+    let trailing = margins.trailingAnchor
+    textField.trailingAnchor.constraint(equalTo: trailing).isActive = true
+    textField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
+    if let label = self.textLabel {
+      textField.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: 20).isActive = true // this will prevent the textfield from growing over the textLabel while typing
+    } else {
+      textField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
     }
+  }
 
-    func getText() -> String? {
-		if let text = textField.text {
-			if text.isEmpty {
-				return nil
-			} else {
-				return self.textField.text
-			}
-		} else {
-			return nil
-		}
-	}
-
-
-	func setText(text: String?) {
-		self.textField.text = text
-	}
-	
-
-	static func makeEmailCell(delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
-        let cell = TextFieldCell(description: "Email", placeholder: "you@example.com")
-        cell.textField.keyboardType = .emailAddress
-        // switch off quicktype
-        cell.textField.autocorrectionType = .no
-        cell.textField.autocapitalizationType = .none
-		cell.textField.delegate = delegate
-        return cell
+  override func setSelected(_ selected: Bool, animated _: Bool) {
+    if selected {
+      textField.becomeFirstResponder()
     }
-
-    static func makePasswordCell(delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
-        let cell = TextFieldCell(description: "Password", placeholder: "your IMAP password")
-        cell.textField.textContentType = UITextContentType.password
-        cell.textField.isSecureTextEntry = true
-		return cell
-    }
-
-    static func makeNameCell(delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
-        let cell = TextFieldCell(description: "Name", placeholder: "new contacts nickname")
-
-        cell.textField.autocapitalizationType = .words
-        cell.textField.autocorrectionType = .no
-        // .namePhonePad doesn't support autocapitalization
-        // see: https://stackoverflow.com/a/36365399
-        // therefore we use .default to capitalize the first character of the name
-        cell.textField.keyboardType = .default
-		cell.textField.delegate = delegate
-
-        return cell
-    }
-
-    static func makeConfigCell(label: String, placeholder: String, delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
-        let cell = TextFieldCell(description: label, placeholder: placeholder)
-
-        cell.textField.autocapitalizationType = .words
-        cell.textField.autocorrectionType = .no
-        // .namePhonePad doesn't support autocapitalization
-        // see: https://stackoverflow.com/a/36365399
-        // therefore we use .default to capitalize the first character of the name
-        cell.textField.keyboardType = .default
-		cell.textField.delegate = delegate
-		return cell
+  }
+
+  func getText() -> String? {
+    if let text = textField.text {
+      if text.isEmpty {
+        return nil
+      } else {
+        return textField.text
+      }
+    } else {
+      return nil
     }
+  }
+
+  func setText(text: String?) {
+    textField.text = text
+  }
+
+  static func makeEmailCell(delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
+    let cell = TextFieldCell(description: "Email", placeholder: "you@example.com")
+    cell.textField.keyboardType = .emailAddress
+    // switch off quicktype
+    cell.textField.autocorrectionType = .no
+    cell.textField.autocapitalizationType = .none
+    cell.textField.delegate = delegate
+    return cell
+  }
+
+  static func makePasswordCell(delegate _: UITextFieldDelegate? = nil) -> TextFieldCell {
+    let cell = TextFieldCell(description: "Password", placeholder: "your IMAP password")
+    cell.textField.textContentType = UITextContentType.password
+    cell.textField.isSecureTextEntry = true
+    return cell
+  }
+
+  static func makeNameCell(delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
+    let cell = TextFieldCell(description: "Name", placeholder: "new contacts nickname")
+
+    cell.textField.autocapitalizationType = .words
+    cell.textField.autocorrectionType = .no
+    // .namePhonePad doesn't support autocapitalization
+    // see: https://stackoverflow.com/a/36365399
+    // therefore we use .default to capitalize the first character of the name
+    cell.textField.keyboardType = .default
+    cell.textField.delegate = delegate
+
+    return cell
+  }
+
+  static func makeConfigCell(label: String, placeholder: String, delegate: UITextFieldDelegate? = nil) -> TextFieldCell {
+    let cell = TextFieldCell(description: label, placeholder: placeholder)
+
+    cell.textField.autocapitalizationType = .words
+    cell.textField.autocorrectionType = .no
+    // .namePhonePad doesn't support autocapitalization
+    // see: https://stackoverflow.com/a/36365399
+    // therefore we use .default to capitalize the first character of the name
+    cell.textField.keyboardType = .default
+    cell.textField.delegate = delegate
+    return cell
+  }
 }

+ 297 - 297
deltachat-ios/TopViews/SettingsController.swift

@@ -13,301 +13,301 @@ import QuickTableViewController
 import UIKit
 
 internal final class SettingsViewController: QuickTableViewController {
-	let documentInteractionController = UIDocumentInteractionController()
-	var backupProgressObserver: Any?
-	var configureProgressObserver: Any?
-
-	private lazy var hudHandler: HudHandler = {
-		let hudHandler = HudHandler(parentView: self.tableView)
-		return hudHandler
-	}()
-
-	override func viewDidLoad() {
-		super.viewDidLoad()
-		title = "Settings"
-		documentInteractionController.delegate = self as? UIDocumentInteractionControllerDelegate
-		setTable()
-	}
-
-	override func viewDidAppear(_ animated: Bool) {
-		super.viewDidAppear(animated)
-		let nc = NotificationCenter.default
-		backupProgressObserver = nc.addObserver(
-			forName: dcNotificationBackupProgress,
-			object: nil,
-			queue: nil
-		) { notification in
-			if let ui = notification.userInfo {
-				if ui["error"] as! Bool {
-					self.hudHandler.setHudError(ui["errorMessage"] as? String)
-				} else if ui["done"] as! Bool {
-					self.hudHandler.setHudDone(callback: nil)
-				} else {
-					self.hudHandler.setHudProgress(ui["progress"] as! Int)
-				}
-			}
-		}
-		configureProgressObserver = nc.addObserver(
-			forName: dcNotificationConfigureProgress,
-			object: nil,
-			queue: nil
-		) { notification in
-			if let ui = notification.userInfo {
-				if ui["error"] as! Bool {
-					self.hudHandler.setHudError(ui["errorMessage"] as? String)
-				} else if ui["done"] as! Bool {
-					self.hudHandler.setHudDone(callback: nil)
-				} else {
-					self.hudHandler.setHudProgress(ui["progress"] as! Int)
-				}
-			}
-		}
-	}
-
-	override func viewWillAppear(_ animated: Bool) {
-		super.viewWillAppear(animated)
-
-		if #available(iOS 11.0, *) {
-			navigationController?.navigationBar.prefersLargeTitles = true
-		}
-	}
-
-	override func viewWillDisappear(_ animated: Bool) {
-		super.viewWillDisappear(animated)
-		if #available(iOS 11.0, *) {
-			navigationController?.navigationBar.prefersLargeTitles = false
-		}
-	}
-
-	override func viewDidDisappear(_ animated: Bool) {
-		super.viewDidDisappear(animated)
-
-		let nc = NotificationCenter.default
-		if let backupProgressObserver = self.backupProgressObserver {
-			nc.removeObserver(backupProgressObserver)
-		}
-		if let configureProgressObserver = self.configureProgressObserver {
-			nc.removeObserver(configureProgressObserver)
-		}
-	}
-
-	private func setTable() {
-		var backupRows = [
-			TapActionRow(text: "Create backup", action: { [weak self] in self?.createBackup($0) }),
-			TapActionRow(text: "Restore from backup", action: { [weak self] in self?.restoreBackup($0)})
-		]
-
-		let deleteRow = TapActionRow(text: "Delete Account", action: { [weak self] in self?.deleteAccount($0)})
-
-		if MRConfig.configured {
-			backupRows.removeLast()
-		}
-
-		tableContents = [
-			Section(
-				title: "User Details",
-				rows: [
-					NavigationRow(text: "Display Name", detailText: .value1(MRConfig.displayname ?? ""), action: editCell()),
-					NavigationRow(text: "Status", detailText: .value1(MRConfig.selfstatus ?? ""), action: editCell()),
-					TapActionRow(text: "Configure my Account", action: { [weak self] in self?.presentAccountSetup($0)})
-				]
-			),
-			Section(
-				title: "Flags",
-				rows: [
-					SwitchRow(text: "E2EE enabled", switchValue: MRConfig.e2eeEnabled, action: editCell()),
-					SwitchRow(text: "Read Receipts", switchValue: MRConfig.mdnsEnabled, action: editCell()),
-					SwitchRow(text: "Watch Inbox", switchValue: MRConfig.inboxWatch, action: editCell()),
-					SwitchRow(text: "Watch Sentbox", switchValue: MRConfig.sentboxWatch, action: editCell()),
-					SwitchRow(text: "Watch Mvbox", switchValue: MRConfig.mvboxWatch, action: editCell()),
-					SwitchRow(text: "Move to Mvbox", switchValue: MRConfig.mvboxMove, action: editCell()),
-					SwitchRow(text: "Save Mime Headers", switchValue: MRConfig.saveMimeHeaders, action: editCell())
-				]
-			),
-
-			Section(
-				title: "Backup",
-				rows: backupRows
-			),
-
-			Section(title: "Danger", rows: [
-				deleteRow
-				])
-		]
-	}
-
-	// FIXME: simplify this method
-	// swiftlint:disable cyclomatic_complexity
-	private func editCell() -> (Row) -> Void {
-		return { [weak self] sender in
-			logger.info("row edit", sender.text)
-
-			let title = sender.text
-			let subtitle: String = sender.detailText?.text ?? ""
-			let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert)
-
-			if title == "Email" {
-				if MRConfig.configured {
-					// Don't change emails in the running system
-					return
-				}
-			}
-
-			if let sender = sender as? SwitchRow {
-				logger.info("got bool switch")
-				let value = sender.switchValue
-
-				switch title {
-				case "E2EE enabled":
-					MRConfig.e2eeEnabled = value
-				case "Read Receipts":
-					MRConfig.mdnsEnabled = value
-				case "Watch Inbox":
-					MRConfig.inboxWatch = value
-				case "Watch Sentbox":
-					MRConfig.sentboxWatch = value
-				case "Watch Mvbox":
-					MRConfig.mvboxWatch = value
-				case "Move to Mvbox":
-					MRConfig.mvboxMove = value
-				case "Save Mime Headers":
-					MRConfig.saveMimeHeaders = value
-				default:
-					logger.info("unknown title", title)
-				}
-				dc_configure(mailboxPointer)
-				return
-			}
-
-			let confirmAction = UIAlertAction(title: "Save", style: .default) { _ in
-				guard let textFields = alertController.textFields,
-					!textFields.isEmpty else {
-						// Could not find textfield
-						return
-				}
-
-				let field = textFields[0]
-
-				// TODO: add field validation
-				var needRefresh = false
-
-				switch title {
-				case "Display Name":
-					MRConfig.displayname = field.text
-					needRefresh = true
-				case "Status":
-					MRConfig.selfstatus = field.text
-					needRefresh = true
-				default:
-					logger.info("unknown title", title)
-				}
-
-				if needRefresh {
-					dc_configure(mailboxPointer)
-					self?.setTable()
-					self?.tableView.reloadData()
-				}
-			}
-
-			let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
-				logger.info("canceled")
-			}
-
-			alertController.addTextField { textField in
-				textField.placeholder = subtitle
-			}
-
-			alertController.addAction(confirmAction)
-			alertController.addAction(cancelAction)
-
-			self?.present(alertController, animated: true, completion: nil)
-		}
-	}
-
-	private func createBackup(_: Row) {
-		// if let documents = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.delta.chat.ios")?.path {
-
-		let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
-		if !documents.isEmpty {
-			logger.info("create backup in \(documents)")
-			hudHandler.showBackupHud("Creating Backup")
-			DispatchQueue.main.async {
-				dc_imex(mailboxPointer, DC_IMEX_EXPORT_BACKUP, documents[0], nil)
-			}
-		} else {
-			logger.error("document directory not found")
-		}
-	}
-
-	private func restoreBackup(_: Row) {
-		logger.info("restoring backup")
-		if MRConfig.configured {
-			return
-		}
-		let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
-		if !documents.isEmpty {
-			logger.info("looking for backup in: \(documents[0])")
-
-			if let file = dc_imex_has_backup(mailboxPointer, documents[0]) {
-				logger.info("restoring backup: \(String(cString: file))")
-
-				hudHandler.showBackupHud("Restoring Backup")
-				dc_imex(mailboxPointer, DC_IMEX_IMPORT_BACKUP, file, nil)
-
-				return
-			}
-
-			let alert = UIAlertController(title: "Can not restore", message: "No Backup found", preferredStyle: .alert)
-			alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
-				self.dismiss(animated: true, completion: nil)
-			}))
-			present(alert, animated: true, completion: nil)
-			return
-		}
-
-		logger.error("no documents directory found")
-	}
-
-	private func configure(_: Row) {
-		hudHandler.showBackupHud("Configuring account")
-		dc_configure(mailboxPointer)
-	}
-
-	private func deleteAccount(_: Row) {
-		logger.info("deleting account")
-		guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
-			return
-		}
-
-		let dbfile = appDelegate.dbfile()
-		let dburl = URL(fileURLWithPath: dbfile, isDirectory: false)
-		let alert = UIAlertController(title: "Delete Account", message: "Are you sure you wante to delete your account data?", preferredStyle: .alert)
-
-		alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { _ in
-			appDelegate.stop()
-			appDelegate.close()
-			do {
-				try FileManager.default.removeItem(at: dburl)
-			} catch {
-				logger.error("failed to delete db: \(error)")
-			}
-
-			appDelegate.open()
-			appDelegate.start()
-
-			// refresh our view
-			self.setTable()
-			self.tableView.reloadData()
-
-			self.dismiss(animated: true, completion: nil)
-		}))
-		alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
-
-		present(alert, animated: true, completion: nil)
-	}
-
-	private func presentAccountSetup(_:Row) {
-		let nav = UINavigationController(rootViewController: AccountSetupController())
-		present(nav, animated: true, completion: nil)
-	}
+  let documentInteractionController = UIDocumentInteractionController()
+  var backupProgressObserver: Any?
+  var configureProgressObserver: Any?
+
+  private lazy var hudHandler: HudHandler = {
+    let hudHandler = HudHandler(parentView: self.tableView)
+    return hudHandler
+  }()
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    title = "Settings"
+    documentInteractionController.delegate = self as? UIDocumentInteractionControllerDelegate
+    setTable()
+  }
+
+  override func viewDidAppear(_ animated: Bool) {
+    super.viewDidAppear(animated)
+    let nc = NotificationCenter.default
+    backupProgressObserver = nc.addObserver(
+      forName: dcNotificationBackupProgress,
+      object: nil,
+      queue: nil
+    ) { notification in
+      if let ui = notification.userInfo {
+        if ui["error"] as! Bool {
+          self.hudHandler.setHudError(ui["errorMessage"] as? String)
+        } else if ui["done"] as! Bool {
+          self.hudHandler.setHudDone(callback: nil)
+        } else {
+          self.hudHandler.setHudProgress(ui["progress"] as! Int)
+        }
+      }
+    }
+    configureProgressObserver = nc.addObserver(
+      forName: dcNotificationConfigureProgress,
+      object: nil,
+      queue: nil
+    ) { notification in
+      if let ui = notification.userInfo {
+        if ui["error"] as! Bool {
+          self.hudHandler.setHudError(ui["errorMessage"] as? String)
+        } else if ui["done"] as! Bool {
+          self.hudHandler.setHudDone(callback: nil)
+        } else {
+          self.hudHandler.setHudProgress(ui["progress"] as! Int)
+        }
+      }
+    }
+  }
+
+  override func viewWillAppear(_ animated: Bool) {
+    super.viewWillAppear(animated)
+
+    if #available(iOS 11.0, *) {
+      navigationController?.navigationBar.prefersLargeTitles = true
+    }
+  }
+
+  override func viewWillDisappear(_ animated: Bool) {
+    super.viewWillDisappear(animated)
+    if #available(iOS 11.0, *) {
+      navigationController?.navigationBar.prefersLargeTitles = false
+    }
+  }
+
+  override func viewDidDisappear(_ animated: Bool) {
+    super.viewDidDisappear(animated)
+
+    let nc = NotificationCenter.default
+    if let backupProgressObserver = self.backupProgressObserver {
+      nc.removeObserver(backupProgressObserver)
+    }
+    if let configureProgressObserver = self.configureProgressObserver {
+      nc.removeObserver(configureProgressObserver)
+    }
+  }
+
+  private func setTable() {
+    var backupRows = [
+      TapActionRow(text: "Create backup", action: { [weak self] in self?.createBackup($0) }),
+      TapActionRow(text: "Restore from backup", action: { [weak self] in self?.restoreBackup($0) }),
+    ]
+
+    let deleteRow = TapActionRow(text: "Delete Account", action: { [weak self] in self?.deleteAccount($0) })
+
+    if MRConfig.configured {
+      backupRows.removeLast()
+    }
+
+    tableContents = [
+      Section(
+        title: "User Details",
+        rows: [
+          NavigationRow(text: "Display Name", detailText: .value1(MRConfig.displayname ?? ""), action: editCell()),
+          NavigationRow(text: "Status", detailText: .value1(MRConfig.selfstatus ?? ""), action: editCell()),
+          TapActionRow(text: "Configure my Account", action: { [weak self] in self?.presentAccountSetup($0) }),
+        ]
+      ),
+      Section(
+        title: "Flags",
+        rows: [
+          SwitchRow(text: "E2EE enabled", switchValue: MRConfig.e2eeEnabled, action: editCell()),
+          SwitchRow(text: "Read Receipts", switchValue: MRConfig.mdnsEnabled, action: editCell()),
+          SwitchRow(text: "Watch Inbox", switchValue: MRConfig.inboxWatch, action: editCell()),
+          SwitchRow(text: "Watch Sentbox", switchValue: MRConfig.sentboxWatch, action: editCell()),
+          SwitchRow(text: "Watch Mvbox", switchValue: MRConfig.mvboxWatch, action: editCell()),
+          SwitchRow(text: "Move to Mvbox", switchValue: MRConfig.mvboxMove, action: editCell()),
+          SwitchRow(text: "Save Mime Headers", switchValue: MRConfig.saveMimeHeaders, action: editCell()),
+        ]
+      ),
+
+      Section(
+        title: "Backup",
+        rows: backupRows
+      ),
+
+      Section(title: "Danger", rows: [
+        deleteRow,
+      ]),
+    ]
+  }
+
+  // FIXME: simplify this method
+  // swiftlint:disable cyclomatic_complexity
+  private func editCell() -> (Row) -> Void {
+    return { [weak self] sender in
+      logger.info("row edit", sender.text)
+
+      let title = sender.text
+      let subtitle: String = sender.detailText?.text ?? ""
+      let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert)
+
+      if title == "Email" {
+        if MRConfig.configured {
+          // Don't change emails in the running system
+          return
+        }
+      }
+
+      if let sender = sender as? SwitchRow {
+        logger.info("got bool switch")
+        let value = sender.switchValue
+
+        switch title {
+        case "E2EE enabled":
+          MRConfig.e2eeEnabled = value
+        case "Read Receipts":
+          MRConfig.mdnsEnabled = value
+        case "Watch Inbox":
+          MRConfig.inboxWatch = value
+        case "Watch Sentbox":
+          MRConfig.sentboxWatch = value
+        case "Watch Mvbox":
+          MRConfig.mvboxWatch = value
+        case "Move to Mvbox":
+          MRConfig.mvboxMove = value
+        case "Save Mime Headers":
+          MRConfig.saveMimeHeaders = value
+        default:
+          logger.info("unknown title", title)
+        }
+        dc_configure(mailboxPointer)
+        return
+      }
+
+      let confirmAction = UIAlertAction(title: "Save", style: .default) { _ in
+        guard let textFields = alertController.textFields,
+          !textFields.isEmpty else {
+          // Could not find textfield
+          return
+        }
+
+        let field = textFields[0]
+
+        // TODO: add field validation
+        var needRefresh = false
+
+        switch title {
+        case "Display Name":
+          MRConfig.displayname = field.text
+          needRefresh = true
+        case "Status":
+          MRConfig.selfstatus = field.text
+          needRefresh = true
+        default:
+          logger.info("unknown title", title)
+        }
+
+        if needRefresh {
+          dc_configure(mailboxPointer)
+          self?.setTable()
+          self?.tableView.reloadData()
+        }
+      }
+
+      let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
+        logger.info("canceled")
+      }
+
+      alertController.addTextField { textField in
+        textField.placeholder = subtitle
+      }
+
+      alertController.addAction(confirmAction)
+      alertController.addAction(cancelAction)
+
+      self?.present(alertController, animated: true, completion: nil)
+    }
+  }
+
+  private func createBackup(_: Row) {
+    // if let documents = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.delta.chat.ios")?.path {
+
+    let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
+    if !documents.isEmpty {
+      logger.info("create backup in \(documents)")
+      hudHandler.showBackupHud("Creating Backup")
+      DispatchQueue.main.async {
+        dc_imex(mailboxPointer, DC_IMEX_EXPORT_BACKUP, documents[0], nil)
+      }
+    } else {
+      logger.error("document directory not found")
+    }
+  }
+
+  private func restoreBackup(_: Row) {
+    logger.info("restoring backup")
+    if MRConfig.configured {
+      return
+    }
+    let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
+    if !documents.isEmpty {
+      logger.info("looking for backup in: \(documents[0])")
+
+      if let file = dc_imex_has_backup(mailboxPointer, documents[0]) {
+        logger.info("restoring backup: \(String(cString: file))")
+
+        hudHandler.showBackupHud("Restoring Backup")
+        dc_imex(mailboxPointer, DC_IMEX_IMPORT_BACKUP, file, nil)
+
+        return
+      }
+
+      let alert = UIAlertController(title: "Can not restore", message: "No Backup found", preferredStyle: .alert)
+      alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
+        self.dismiss(animated: true, completion: nil)
+      }))
+      present(alert, animated: true, completion: nil)
+      return
+    }
+
+    logger.error("no documents directory found")
+  }
+
+  private func configure(_: Row) {
+    hudHandler.showBackupHud("Configuring account")
+    dc_configure(mailboxPointer)
+  }
+
+  private func deleteAccount(_: Row) {
+    logger.info("deleting account")
+    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
+      return
+    }
+
+    let dbfile = appDelegate.dbfile()
+    let dburl = URL(fileURLWithPath: dbfile, isDirectory: false)
+    let alert = UIAlertController(title: "Delete Account", message: "Are you sure you wante to delete your account data?", preferredStyle: .alert)
+
+    alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { _ in
+      appDelegate.stop()
+      appDelegate.close()
+      do {
+        try FileManager.default.removeItem(at: dburl)
+      } catch {
+        logger.error("failed to delete db: \(error)")
+      }
+
+      appDelegate.open()
+      appDelegate.start()
+
+      // refresh our view
+      self.setTable()
+      self.tableView.reloadData()
+
+      self.dismiss(animated: true, completion: nil)
+    }))
+    alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
+
+    present(alert, animated: true, completion: nil)
+  }
+
+  private func presentAccountSetup(_: Row) {
+    let nav = UINavigationController(rootViewController: AccountSetupController())
+    present(nav, animated: true, completion: nil)
+  }
 }

+ 2 - 1
deltachat-ios/Utils.swift

@@ -51,7 +51,8 @@ struct Utils {
     let lenArray = dc_array_get_cnt(inputArray)
     if lenArray <= skipEnd || lenArray == 0 {
       dc_array_unref(inputArray)
-      return [] }
+      return []
+    }
 
     let start = lenArray - 1 - skipEnd
     let end = max(0, start - len)

+ 53 - 55
deltachat-ios/Wrapper.swift

@@ -520,7 +520,7 @@ class MRConfig {
     let vStr = value ? "1" : "0"
     setOptStr(key, vStr)
   }
-    
+
   private class func getInt(_ key: String) -> Int {
     let vStr = getOptStr(key)
     if vStr == nil {
@@ -644,69 +644,67 @@ class MRConfig {
       return getOptStr("send_port")
     }
   }
-    
-      /**
+
+  /**
    * IMAP-/SMTP-flags as a combination of DC_LP flags, guessed if left out
    */
-    
 
   private class var serverFlags: Int {
     set {
       setOptStr("server_flags", "\(newValue)")
     }
     get {
-        if let str = getOptStr("server_flags") {
-            return Int(str) ?? 0
-        } else {
-            return 0
-        }
+      if let str = getOptStr("server_flags") {
+        return Int(str) ?? 0
+      } else {
+        return 0
+      }
     }
   }
-    
-    
-    class func setImapSecurity(imapFlags flags: Int) {
-        var sf = serverFlags
-        sf = sf & ~0x700 // TODO: should be DC_LP_IMAP_SOCKET_FLAGS - could not be found
-        sf = sf | flags
-        serverFlags = sf
-    }
-    
-    class func setSmtpSecurity(smptpFlags flags: Int) {
-        var sf = serverFlags
-        sf = sf & ~0x70000 // TODO: should be DC_LP_SMTP_SOCKET_FLAGS - could not be found
-        sf = sf | flags
-        serverFlags = sf
-    }
-
-	class func setAuthFlags(flags: Int) {
-        var sf = serverFlags
-        sf = sf & ~0x6 // TODO: should be DC_LP_AUTH_FLAGS - could not be found
-        sf = sf | flags
-        serverFlags = sf
-    }
-
-	// returns one of DC_LP_IMAP_SOCKET_STARTTLS, DC_LP_IMAP_SOCKET_SSL,
-	class func getImapSecurity() -> Int {
-		var sf = serverFlags
-		sf = sf & 0x700
-		return sf
-	}
-
-	// returns one of DC_LP_SMTP_SOCKET_STARTTLS, DC_LP_SMTP_SOCKET_SSL,
-	class func getSmtpSecurity() -> Int {
-		var sf = serverFlags
-		sf = sf & 0x70000
-		return sf
-	}
-
-	// returns on of DC_LP_AUTH_OAUTH2 or 0
-	class func getAuthFlags() -> Int {
-		var sf = serverFlags
-		sf = sf & 0x6
-		serverFlags = sf
-		return sf
-	}
-    
+
+  class func setImapSecurity(imapFlags flags: Int) {
+    var sf = serverFlags
+    sf = sf & ~0x700 // TODO: should be DC_LP_IMAP_SOCKET_FLAGS - could not be found
+    sf = sf | flags
+    serverFlags = sf
+  }
+
+  class func setSmtpSecurity(smptpFlags flags: Int) {
+    var sf = serverFlags
+    sf = sf & ~0x70000 // TODO: should be DC_LP_SMTP_SOCKET_FLAGS - could not be found
+    sf = sf | flags
+    serverFlags = sf
+  }
+
+  class func setAuthFlags(flags: Int) {
+    var sf = serverFlags
+    sf = sf & ~0x6 // TODO: should be DC_LP_AUTH_FLAGS - could not be found
+    sf = sf | flags
+    serverFlags = sf
+  }
+
+  // returns one of DC_LP_IMAP_SOCKET_STARTTLS, DC_LP_IMAP_SOCKET_SSL,
+  class func getImapSecurity() -> Int {
+    var sf = serverFlags
+    sf = sf & 0x700
+    return sf
+  }
+
+  // returns one of DC_LP_SMTP_SOCKET_STARTTLS, DC_LP_SMTP_SOCKET_SSL,
+  class func getSmtpSecurity() -> Int {
+    var sf = serverFlags
+    sf = sf & 0x70000
+    return sf
+  }
+
+  // returns on of DC_LP_AUTH_OAUTH2 or 0
+  class func getAuthFlags() -> Int {
+    var sf = serverFlags
+    sf = sf & 0x6
+    serverFlags = sf
+    return sf
+  }
+
   /**
    * Own name to use when sending messages. MUAs are allowed to spread this way eg. using CC, defaults to empty
    */
@@ -828,7 +826,7 @@ class MRConfig {
       return getBool("show_emails")
     }
   }
-  
+
   /**
    * 1=save mime headers and make dc_get_mime_headers() work for subsequent calls, 0=do not save mime headers (default)
    */

+ 201 - 201
deltachat-ios/events.swift

@@ -20,205 +20,205 @@ let dcNotificationViewChat = Notification.Name(rawValue: "MrEventViewChat")
 @_silgen_name("callbackSwift")
 
 public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLong, data1String: UnsafePointer<Int8>, data2String: UnsafePointer<Int8>) -> UnsafePointer<Int8>? {
-	switch event {
-	case DC_EVENT_HTTP_POST:
-
-		let urlString = String(cString: data1String)
-		logger.info("network: http post: \(urlString)")
-
-		let base = String(urlString.split(separator: "?")[0])
-
-		guard let baseUrl = URL(string: base), let url = URL(string: urlString) else {
-			return nil
-		}
-		var request = URLRequest(url: baseUrl)
-		request.httpMethod = "POST"
-
-		if let params = url.queryParameters {
-			request.httpBody = params.percentEscaped().data(using: .utf8)
-		}
-
-		let (data, _, _) = URLSession.shared.synchronousDataTask(request: request) // returns (data, response, error)
-		guard let receivedData = data, let dataString = String(bytes: receivedData, encoding: .utf8) else {
-			return nil
-		}
-		let p = UnsafePointer(strdup(dataString))
-		return p
-	case DC_EVENT_HTTP_GET:
-		let urlString = String(cString: data1String)
-		logger.info("network: http get: \(urlString)")
-		guard let url = URL(string: urlString) else {
-			return nil
-		}
-		guard let configText = try? String(contentsOf: url) else {
-			return nil
-		}
-
-		// see the strdup tip here: https://oleb.net/blog/2016/10/swift-array-of-c-strings/#alternative-strdup-and-free
-		let p = UnsafePointer(strdup(configText))
-		return p
-	case DC_EVENT_INFO:
-		let s = String(cString: data2String)
-		logger.info("event: \(s)")
-	case DC_EVENT_WARNING:
-		let s = String(cString: data2String)
-		logger.warning("event: \(s)")
-	case DC_EVENT_ERROR:
-		let s = String(cString: data2String)
-		AppDelegate.lastErrorDuringConfig = s
-		logger.error("event: \(s)")
-		// TODO:
-		// check online state, return
-		// - 0 when online
-	// - 1 when offline
-	case DC_EVENT_CONFIGURE_PROGRESS:
-		logger.info("configure progress: \(Int(data1)) \(Int(data2))")
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			let done = Int(data1) == 1000
-
-			nc.post(
-				name: dcNotificationConfigureProgress,
-				object: nil,
-				userInfo: [
-					"progress": Int(data1),
-					"error": Int(data1) == 0,
-					"done": done,
-					"errorMessage": AppDelegate.lastErrorDuringConfig,
-				]
-			)
-
-			if done {
-				UserDefaults.standard.set(true, forKey: Constants.Keys.deltachatUserProvidedCredentialsKey)
-				UserDefaults.standard.synchronize()
-				AppDelegate.appCoordinator.setupInnerViewControllers()
-				AppDelegate.lastErrorDuringConfig = nil
-			}
-		}
-	case DC_EVENT_ERROR_NETWORK:
-		let msg = String(cString: data2String)
-		if data1 == 1 {
-			AppDelegate.lastErrorDuringConfig = msg
-			logger.error("network: \(msg)")
-		} else {
-			logger.warning("network: \(msg)")
-		}
-
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			DispatchQueue.main.async {
-				nc.post(name: dcNotificationStateChanged,
-						object: nil,
-						userInfo: ["state": "offline"])
-			}
-		}
-	case DC_EVENT_IMAP_CONNECTED, DC_EVENT_SMTP_CONNECTED:
-		logger.warning("network: \(String(cString: data2String))")
-
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			nc.post(name: dcNotificationStateChanged,
-					object: nil,
-					userInfo: ["state": "online"])
-		}
-	case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED:
-		logger.info("change: \(event)")
-
-		let nc = NotificationCenter.default
-
-		DispatchQueue.main.async {
-			nc.post(
-				name: dcNotificationChanged,
-				object: nil,
-				userInfo: [
-					"message_id": Int(data2),
-					"chat_id": Int(data1),
-					"date": Date(),
-				]
-			)
-		}
-	case DC_EVENT_INCOMING_MSG:
-		let nc = NotificationCenter.default
-		let userInfo = [
-			"message_id": Int(data2),
-			"chat_id": Int(data1),
-		]
-
-		DispatchQueue.main.async {
-			nc.post(name: dcNotificationIncoming,
-					object: nil,
-					userInfo: userInfo)
-
-			let content = UNMutableNotificationContent()
-			let msg = MRMessage(id: Int(data2))
-			content.title = msg.fromContact.name
-			content.body = msg.summary(chars: 40) ?? ""
-			content.badge = 1
-			content.userInfo = userInfo
-			content.sound = .default
-
-			let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
-
-			let request = UNNotificationRequest(identifier: Constants.notificationIdentifier, content: content, trigger: trigger)
-			UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
-			logger.info("notifications: added \(content)")
-		}
-	case DC_EVENT_SMTP_MESSAGE_SENT:
-		logger.info("network: \(String(cString: data2String))")
-	case DC_EVENT_MSG_DELIVERED:
-		logger.info("message delivered: \(data1)-\(data2)")
-	case DC_EVENT_IMEX_PROGRESS:
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			nc.post(
-				name: dcNotificationBackupProgress,
-				object: nil,
-				userInfo: [
-					"progress": Int(data1),
-					"error": Int(data1) == 0,
-					"done": Int(data1) == 1000,
-					"errorMessage": AppDelegate.lastErrorDuringConfig,
-				]
-			)
-		}
-	case DC_EVENT_IMEX_FILE_WRITTEN:
-		logger.info("backup file written: \(String(cString: data1String))")
-
-	case DC_EVENT_SECUREJOIN_INVITER_PROGRESS:
-		logger.info("securejoin inviter progress \(data1)")
-
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			nc.post(
-				name: dcNotificationSecureInviterProgress,
-				object: nil,
-				userInfo: [
-					"progress": Int(data2),
-					"error": Int(data2) == 0,
-					"done": Int(data2) == 1000,
-				]
-			)
-		}
-	case DC_EVENT_SECUREJOIN_JOINER_PROGRESS:
-		logger.info("securejoin joiner progress \(data1)")
-		let nc = NotificationCenter.default
-		DispatchQueue.main.async {
-			nc.post(
-				name: dcNotificationSecureJoinerProgress,
-				object: nil,
-				userInfo: [
-					"progress": Int(data2),
-					"error": Int(data2) == 0,
-					"done": Int(data2) == 1000,
-				]
-			)
-		}
-	case DC_EVENT_GET_STRING:
-		// nothing to do for now
-		break
-	default:
-		logger.warning("unknown event: \(event)")
-	}
-
-	return nil
+  switch event {
+  case DC_EVENT_HTTP_POST:
+
+    let urlString = String(cString: data1String)
+    logger.info("network: http post: \(urlString)")
+
+    let base = String(urlString.split(separator: "?")[0])
+
+    guard let baseUrl = URL(string: base), let url = URL(string: urlString) else {
+      return nil
+    }
+    var request = URLRequest(url: baseUrl)
+    request.httpMethod = "POST"
+
+    if let params = url.queryParameters {
+      request.httpBody = params.percentEscaped().data(using: .utf8)
+    }
+
+    let (data, _, _) = URLSession.shared.synchronousDataTask(request: request) // returns (data, response, error)
+    guard let receivedData = data, let dataString = String(bytes: receivedData, encoding: .utf8) else {
+      return nil
+    }
+    let p = UnsafePointer(strdup(dataString))
+    return p
+  case DC_EVENT_HTTP_GET:
+    let urlString = String(cString: data1String)
+    logger.info("network: http get: \(urlString)")
+    guard let url = URL(string: urlString) else {
+      return nil
+    }
+    guard let configText = try? String(contentsOf: url) else {
+      return nil
+    }
+
+    // see the strdup tip here: https://oleb.net/blog/2016/10/swift-array-of-c-strings/#alternative-strdup-and-free
+    let p = UnsafePointer(strdup(configText))
+    return p
+  case DC_EVENT_INFO:
+    let s = String(cString: data2String)
+    logger.info("event: \(s)")
+  case DC_EVENT_WARNING:
+    let s = String(cString: data2String)
+    logger.warning("event: \(s)")
+  case DC_EVENT_ERROR:
+    let s = String(cString: data2String)
+    AppDelegate.lastErrorDuringConfig = s
+    logger.error("event: \(s)")
+  // TODO:
+  // check online state, return
+  // - 0 when online
+  // - 1 when offline
+  case DC_EVENT_CONFIGURE_PROGRESS:
+    logger.info("configure progress: \(Int(data1)) \(Int(data2))")
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      let done = Int(data1) == 1000
+
+      nc.post(
+        name: dcNotificationConfigureProgress,
+        object: nil,
+        userInfo: [
+          "progress": Int(data1),
+          "error": Int(data1) == 0,
+          "done": done,
+          "errorMessage": AppDelegate.lastErrorDuringConfig,
+        ]
+      )
+
+      if done {
+        UserDefaults.standard.set(true, forKey: Constants.Keys.deltachatUserProvidedCredentialsKey)
+        UserDefaults.standard.synchronize()
+        AppDelegate.appCoordinator.setupInnerViewControllers()
+        AppDelegate.lastErrorDuringConfig = nil
+      }
+    }
+  case DC_EVENT_ERROR_NETWORK:
+    let msg = String(cString: data2String)
+    if data1 == 1 {
+      AppDelegate.lastErrorDuringConfig = msg
+      logger.error("network: \(msg)")
+    } else {
+      logger.warning("network: \(msg)")
+    }
+
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      DispatchQueue.main.async {
+        nc.post(name: dcNotificationStateChanged,
+                object: nil,
+                userInfo: ["state": "offline"])
+      }
+    }
+  case DC_EVENT_IMAP_CONNECTED, DC_EVENT_SMTP_CONNECTED:
+    logger.warning("network: \(String(cString: data2String))")
+
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      nc.post(name: dcNotificationStateChanged,
+              object: nil,
+              userInfo: ["state": "online"])
+    }
+  case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED:
+    logger.info("change: \(event)")
+
+    let nc = NotificationCenter.default
+
+    DispatchQueue.main.async {
+      nc.post(
+        name: dcNotificationChanged,
+        object: nil,
+        userInfo: [
+          "message_id": Int(data2),
+          "chat_id": Int(data1),
+          "date": Date(),
+        ]
+      )
+    }
+  case DC_EVENT_INCOMING_MSG:
+    let nc = NotificationCenter.default
+    let userInfo = [
+      "message_id": Int(data2),
+      "chat_id": Int(data1),
+    ]
+
+    DispatchQueue.main.async {
+      nc.post(name: dcNotificationIncoming,
+              object: nil,
+              userInfo: userInfo)
+
+      let content = UNMutableNotificationContent()
+      let msg = MRMessage(id: Int(data2))
+      content.title = msg.fromContact.name
+      content.body = msg.summary(chars: 40) ?? ""
+      content.badge = 1
+      content.userInfo = userInfo
+      content.sound = .default
+
+      let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
+
+      let request = UNNotificationRequest(identifier: Constants.notificationIdentifier, content: content, trigger: trigger)
+      UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
+      logger.info("notifications: added \(content)")
+    }
+  case DC_EVENT_SMTP_MESSAGE_SENT:
+    logger.info("network: \(String(cString: data2String))")
+  case DC_EVENT_MSG_DELIVERED:
+    logger.info("message delivered: \(data1)-\(data2)")
+  case DC_EVENT_IMEX_PROGRESS:
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      nc.post(
+        name: dcNotificationBackupProgress,
+        object: nil,
+        userInfo: [
+          "progress": Int(data1),
+          "error": Int(data1) == 0,
+          "done": Int(data1) == 1000,
+          "errorMessage": AppDelegate.lastErrorDuringConfig,
+        ]
+      )
+    }
+  case DC_EVENT_IMEX_FILE_WRITTEN:
+    logger.info("backup file written: \(String(cString: data1String))")
+
+  case DC_EVENT_SECUREJOIN_INVITER_PROGRESS:
+    logger.info("securejoin inviter progress \(data1)")
+
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      nc.post(
+        name: dcNotificationSecureInviterProgress,
+        object: nil,
+        userInfo: [
+          "progress": Int(data2),
+          "error": Int(data2) == 0,
+          "done": Int(data2) == 1000,
+        ]
+      )
+    }
+  case DC_EVENT_SECUREJOIN_JOINER_PROGRESS:
+    logger.info("securejoin joiner progress \(data1)")
+    let nc = NotificationCenter.default
+    DispatchQueue.main.async {
+      nc.post(
+        name: dcNotificationSecureJoinerProgress,
+        object: nil,
+        userInfo: [
+          "progress": Int(data2),
+          "error": Int(data2) == 0,
+          "done": Int(data2) == 1000,
+        ]
+      )
+    }
+  case DC_EVENT_GET_STRING:
+    // nothing to do for now
+    break
+  default:
+    logger.warning("unknown event: \(event)")
+  }
+
+  return nil
 }