// // XCallbackURL.swift // Tusker // // Created by Shadowfacts on 9/23/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit typealias XCBAction = (_ url: XCBRequest, _ session: XCBSession, _ silent: Bool?) -> Void struct XCBRequestSpec { let path: String let type: XCBActionType let arguments: [String: Bool] let canRunSilently: Bool let action: XCBAction init(type: XCBActionType, arguments: [String: Bool], canRunSilently: Bool, action: @escaping XCBAction) { self.path = type.path self.type = type self.canRunSilently = canRunSilently self.action = action var arguments = arguments if canRunSilently { arguments["silent"] = true } arguments["json"] = true self.arguments = arguments } func handle(request: XCBRequest) -> Bool { let session = XCBManager.createSession(type: type, request: request) if canRunSilently && request.silent { if let source = request.source { let permission = Preferences.shared.silentActions[source] ?? .undecided switch permission { case .accepted: action(request, session, true) case .rejected: action(request, session, false) case .undecided: let alert = UIAlertController(title: "\(source) wants to perform actions silently", message: "Accepting will allow \(source) to perform actions without your confirmation, rejecting will always prompt for confirmation.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Accept", style: .default, handler: { (_) in Preferences.shared.silentActions[source] = .accepted self.action(request, session, true) })) alert.addAction(UIAlertAction(title: "Reject", style: .default, handler: { (_) in Preferences.shared.silentActions[source] = .rejected self.action(request, session, false) })) UIApplication.shared.keyWindow!.rootViewController!.present(alert, animated: true) } } else { session.complete(with: .error, additionalData: [ "error": "Cannot perform silent action without source app, x-source parameter must be specified." ]) } } else { action(request, session, nil) } return true } } extension XCBRequestSpec { func matches(_ components: URLComponents) -> Bool { guard path == components.path else { return false } for (name, optional) in arguments { if (!optional && components.queryItems?.first(where: { $0.name == name }) == nil) { return false } } return true } }