Browse Source

appDelegate sends aAuth2 token via notification to AccountSetupController

Bastian van de Wetering 6 years ago
parent
commit
88fd5d4530

+ 27 - 7
deltachat-ios/AccountSetupController.swift

@@ -7,6 +7,7 @@
 //
 
 import UIKit
+import SafariServices
 
 class AccountSetupController: UITableViewController {
 
@@ -260,29 +261,36 @@ class AccountSetupController: UITableViewController {
 
 	}
 
-	@objc func loginButtonPressed() {
+	@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: nil)
+		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
+		let password = passwordCell.getText()  ?? "" // empty passwords are ok -> for oauth there is no password needed
+		login(emailAddress: emailAddress, password: password)
 
-		MRConfig.addr = emailAddress
-		MRConfig.mailPw = passWord
-		evaluluateAdvancedSetup()	// this will set MRConfig related to advanced fields
+	}
 
+	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)
 	}
@@ -310,7 +318,9 @@ class AccountSetupController: UITableViewController {
 
 			let oAuthAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
 			let confirm = UIAlertAction(title: "Confirm", style: .default, handler: {
-				_ in
+				[unowned self] _ in
+				let nc = NotificationCenter.default
+				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: {
@@ -328,6 +338,16 @@ class AccountSetupController: UITableViewController {
 		}
 	}
 
+	@objc func oauthLoginApproved(notification: Notification) {
+
+		guard let userInfo = notification.userInfo, let token = userInfo["token"] as? String, let emailAddress = emailCell.getText() else {
+			return
+			
+		}
+		MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
+		login(emailAddress: emailAddress, password: token, skipAdvanceSetup: true)
+	}
+
 	private func launchOAuthBrowserWindow(url: URL) {
 //		let embeddedSafari = SFSafariViewController(url: url)
 //		self.navigationController?.pushViewController(embeddedSafari, animated: true)

+ 11 - 2
deltachat-ios/AppDelegate.swift

@@ -47,8 +47,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 		return []
 	}
 
-	func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
-		print(url)
+	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
 	}
 

+ 33 - 0
deltachat-ios/Extensions/Extensions.swift

@@ -22,3 +22,36 @@ extension UIColor {
 	}
 
 }
+
+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
+		}
+	}
+}
+
+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: "&")
+	}
+}
+
+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
+	}()
+}

+ 9 - 0
deltachat-ios/Info.plist

@@ -55,5 +55,14 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+	<key>CFBundleURLTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>chat.delta</string>
+			</array>
+		</dict>
+	</array>
 </dict>
 </plist>

+ 2 - 2
deltachat-ios/Wrapper.swift

@@ -679,8 +679,8 @@ class MRConfig {
         sf = sf | flags
         serverFlags = sf
     }
-    
-    class func setAuthFlags(flags: Int) {
+
+	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

+ 219 - 178
deltachat-ios/events.swift

@@ -20,182 +20,223 @@ 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_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 data: Data?
+		var repsonse: URLResponse?
+		var error: Error?
+
+		var request = URLRequest(url: baseUrl)
+		request.httpMethod = "POST"
+
+		if let params = url.queryParameters {
+			request.httpBody = params.percentEscaped().data(using: .utf8)
+		}
+
+		let semaphore = DispatchSemaphore(value: 0)
+
+		let task = URLSession.shared.dataTask(with: request) {
+			data = $0
+			repsonse = $1
+			error = $2
+
+			semaphore.signal()
+		}
+		task.resume()
+		_ = semaphore.wait(timeout: .distantFuture)
+
+
+		guard let receivedData = data, let dataString = String(bytes: receivedData, encoding: .utf8) else {
+			return nil
+		}
+		print(dataString)
+		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
 }