diff --git a/WireGuard.xcodeproj/project.pbxproj b/WireGuard.xcodeproj/project.pbxproj index 1542ffa..55956de 100644 --- a/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard.xcodeproj/project.pbxproj @@ -34,6 +34,11 @@ 4A4BAD2320B6026900F12B28 /* Interface+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4BAD1F20B6026900F12B28 /* Interface+CoreDataClass.swift */; }; 4A61D82E20D98CE2006C7A76 /* WireGuardNetworkExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4A61D82620D98CE1006C7A76 /* WireGuardNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 4A61D83520D98D25006C7A76 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A61D83420D98D25006C7A76 /* NetworkExtension.framework */; }; + 4A8A2296215B77D300736141 /* AppCoordinator+QRScanViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A2295215B77D300736141 /* AppCoordinator+QRScanViewControllerDelegate.swift */; }; + 4A8A2298215B780600736141 /* AppCoordinator+SettingsTableViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A2297215B780600736141 /* AppCoordinator+SettingsTableViewControllerDelegate.swift */; }; + 4A8A229A215B782E00736141 /* AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A2299215B782D00736141 /* AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift */; }; + 4A8A229C215B787E00736141 /* AppCoordinator+TunnelsTableViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A229B215B787E00736141 /* AppCoordinator+TunnelsTableViewControllerDelegate.swift */; }; + 4A8A229E215B793C00736141 /* AppCoordinator+TunnelInfoTableViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8A229D215B793C00736141 /* AppCoordinator+TunnelInfoTableViewControllerDelegate.swift */; }; 4A8AABD820B6A79100B6D8C1 /* UITableView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8AABD720B6A79100B6D8C1 /* UITableView+WireGuard.swift */; }; 4AADEA2B212616F7008C24FD /* String+Arrays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA1D50F2124D80C00DBA2E6 /* String+Arrays.swift */; }; 4ABF718E214D8B0300A1E0BF /* TunnelInfoTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABF718D214D8B0300A1E0BF /* TunnelInfoTableViewController.swift */; }; @@ -123,6 +128,11 @@ 4A61D82B20D98CE2006C7A76 /* WireGuardNetworkExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WireGuardNetworkExtension.entitlements; sourceTree = ""; }; 4A61D83320D98D07006C7A76 /* WireGuard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WireGuard.entitlements; sourceTree = ""; }; 4A61D83420D98D25006C7A76 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 4A8A2295215B77D300736141 /* AppCoordinator+QRScanViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+QRScanViewControllerDelegate.swift"; sourceTree = ""; }; + 4A8A2297215B780600736141 /* AppCoordinator+SettingsTableViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+SettingsTableViewControllerDelegate.swift"; sourceTree = ""; }; + 4A8A2299215B782D00736141 /* AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift"; sourceTree = ""; }; + 4A8A229B215B787E00736141 /* AppCoordinator+TunnelsTableViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+TunnelsTableViewControllerDelegate.swift"; sourceTree = ""; }; + 4A8A229D215B793C00736141 /* AppCoordinator+TunnelInfoTableViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppCoordinator+TunnelInfoTableViewControllerDelegate.swift"; sourceTree = ""; }; 4A8AABD720B6A79100B6D8C1 /* UITableView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+WireGuard.swift"; sourceTree = ""; }; 4ABF718D214D8B0300A1E0BF /* TunnelInfoTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelInfoTableViewController.swift; sourceTree = ""; }; 4ABFFE9D212D399F00107136 /* WireGuard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WireGuard-Bridging-Header.h"; sourceTree = ""; }; @@ -248,6 +258,11 @@ 4A4BAD0D20B5F6C300F12B28 /* Coordinator.swift */, 4A4BAD0F20B5F6EC00F12B28 /* RootCoordinator.swift */, 4A4BAD0B20B5F6AA00F12B28 /* AppCoordinator.swift */, + 4A8A229D215B793C00736141 /* AppCoordinator+TunnelInfoTableViewControllerDelegate.swift */, + 4A8A229B215B787E00736141 /* AppCoordinator+TunnelsTableViewControllerDelegate.swift */, + 4A8A2295215B77D300736141 /* AppCoordinator+QRScanViewControllerDelegate.swift */, + 4A8A2297215B780600736141 /* AppCoordinator+SettingsTableViewControllerDelegate.swift */, + 4A8A2299215B782D00736141 /* AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift */, ); path = Coordinators; sourceTree = ""; @@ -585,16 +600,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4A8A229A215B782E00736141 /* AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift in Sources */, 4A4BAD0C20B5F6AA00F12B28 /* AppCoordinator.swift in Sources */, 4A4BAD2220B6026900F12B28 /* Interface+CoreDataProperties.swift in Sources */, + 4A8A2298215B780600736141 /* AppCoordinator+SettingsTableViewControllerDelegate.swift in Sources */, 4AEAC32B20F14BA9007B67AB /* Log.swift in Sources */, + 4A8A229E215B793C00736141 /* AppCoordinator+TunnelInfoTableViewControllerDelegate.swift in Sources */, 4A4BAD1320B5F82400F12B28 /* Identifyable.swift in Sources */, 4A4BAD1720B5F8DE00F12B28 /* WireGuard.xcdatamodeld in Sources */, 5FCC4347212B3E2C009A9C58 /* Attribute.swift in Sources */, 4A4BAD1A20B5F8FF00F12B28 /* Tunnel+CoreDataClass.swift in Sources */, 4A4BACE820B5F1BF00F12B28 /* TunnelsTableViewController.swift in Sources */, 4A4BAD1020B5F6EC00F12B28 /* RootCoordinator.swift in Sources */, + 4A8A2296215B77D300736141 /* AppCoordinator+QRScanViewControllerDelegate.swift in Sources */, 4A4351592124956200261999 /* Validators.swift in Sources */, + 4A8A229C215B787E00736141 /* AppCoordinator+TunnelsTableViewControllerDelegate.swift in Sources */, 5FA1D4CB21249F7D00DBA2E6 /* Peer+Extension.swift in Sources */, 5FA1D5122124DA6400DBA2E6 /* String+Base64.swift in Sources */, 4AC5462E2116306F00749D21 /* Tunnel+Extension.swift in Sources */, diff --git a/WireGuard/Coordinators/AppCoordinator+QRScanViewControllerDelegate.swift b/WireGuard/Coordinators/AppCoordinator+QRScanViewControllerDelegate.swift new file mode 100644 index 0000000..4e239d5 --- /dev/null +++ b/WireGuard/Coordinators/AppCoordinator+QRScanViewControllerDelegate.swift @@ -0,0 +1,16 @@ +// +// Copyright © 2018 WireGuard LLC. All rights reserved. +// + +import Foundation + +extension AppCoordinator: QRScanViewControllerDelegate { + func didSave(tunnel: Tunnel, qrScanViewController: QRScanViewController) { + qrScanViewController.navigationController?.popViewController(animated: true) + showTunnelInfoViewController(tunnel: tunnel, context: tunnel.managedObjectContext!) + } + + func didCancel(qrScanViewController: QRScanViewController) { + qrScanViewController.navigationController?.popViewController(animated: true) + } +} diff --git a/WireGuard/Coordinators/AppCoordinator+SettingsTableViewControllerDelegate.swift b/WireGuard/Coordinators/AppCoordinator+SettingsTableViewControllerDelegate.swift new file mode 100644 index 0000000..0e09386 --- /dev/null +++ b/WireGuard/Coordinators/AppCoordinator+SettingsTableViewControllerDelegate.swift @@ -0,0 +1,17 @@ +// +// Copyright © 2018 WireGuard LLC. All rights reserved. +// + +import UIKit +import PromiseKit +import NetworkExtension + +enum GoVersionCoordinatorError: Error { + case noSession +} + +extension AppCoordinator: SettingsTableViewControllerDelegate { + func exportTunnels(settingsTableViewController: SettingsTableViewController, sourceView: UIView) { + self.exportConfigs(sourceView: sourceView) + } +} diff --git a/WireGuard/Coordinators/AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift b/WireGuard/Coordinators/AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift new file mode 100644 index 0000000..0b96762 --- /dev/null +++ b/WireGuard/Coordinators/AppCoordinator+TunnelConfigurationTableViewControllerDelegate.swift @@ -0,0 +1,11 @@ +// +// Copyright © 2018 WireGuard LLC. All rights reserved. +// + +import Foundation + +extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate { + func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) { + saveTunnel(tunnel) + } +} diff --git a/WireGuard/Coordinators/AppCoordinator+TunnelInfoTableViewControllerDelegate.swift b/WireGuard/Coordinators/AppCoordinator+TunnelInfoTableViewControllerDelegate.swift new file mode 100644 index 0000000..651cce5 --- /dev/null +++ b/WireGuard/Coordinators/AppCoordinator+TunnelInfoTableViewControllerDelegate.swift @@ -0,0 +1,19 @@ +// +// Copyright © 2018 WireGuard LLC. All rights reserved. +// + +import Foundation + +extension AppCoordinator: TunnelInfoTableViewControllerDelegate { + func configure(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) { + print("configure tunnel \(tunnel)") + let editContext = persistentContainer.newBackgroundContext() + var backgroundTunnel: Tunnel? + editContext.performAndWait { + backgroundTunnel = editContext.object(with: tunnel.objectID) as? Tunnel + } + + showTunnelConfigurationViewController(tunnel: backgroundTunnel, context: editContext) + } + +} diff --git a/WireGuard/Coordinators/AppCoordinator+TunnelsTableViewControllerDelegate.swift b/WireGuard/Coordinators/AppCoordinator+TunnelsTableViewControllerDelegate.swift new file mode 100644 index 0000000..5506641 --- /dev/null +++ b/WireGuard/Coordinators/AppCoordinator+TunnelsTableViewControllerDelegate.swift @@ -0,0 +1,196 @@ +// +// Copyright © 2018 WireGuard LLC. All rights reserved. +// + +import UIKit +import NetworkExtension +import os.log + +import MobileCoreServices + +import ZIPFoundation +import PromiseKit + +extension AppCoordinator: TunnelsTableViewControllerDelegate { + func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus { + let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession + return session?.status ?? .invalid + } + + func addProvider(tunnelsTableViewController: TunnelsTableViewController) { + let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from file or archive", comment: ""), style: .default) { [unowned self] _ in + self.addProviderFromFile() + }) + actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from QR code", comment: ""), style: .default) { [unowned self] _ in + self.addProviderWithQRScan() + }) + actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from scratch", comment: ""), style: .default) { [unowned self] _ in + self.addProviderManually() + }) + actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel)) + + tunnelsTableViewController.present(actionSheet, animated: true, completion: nil) + } + + func addProviderFromFile() { + let documentPickerController = UIDocumentPickerViewController(documentTypes: [String(kUTTypeZipArchive), "com.wireguard.config.quick"], in: .import) + documentPickerController.delegate = documentPickerDelegateObject + tunnelsTableViewController.present(documentPickerController, animated: true, completion: nil) + } + + func addProviderManually() { + let addContext = persistentContainer.newBackgroundContext() + showTunnelConfigurationViewController(tunnel: nil, context: addContext) + } + + func addProviderWithQRScan() { + let addContext = persistentContainer.newBackgroundContext() + + let qrScanViewController = storyboard.instantiateViewController(type: QRScanViewController.self) + + qrScanViewController.configure(context: addContext, delegate: self) + + self.navigationController.pushViewController(qrScanViewController, animated: true) + } + + func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { + _ = refreshProviderManagers().then { () -> Promise in + let manager = self.providerManager(for: tunnel)! + let block = { + switch manager.connection.status { + case .invalid, .disconnected: + self.connect(tunnel: tunnel) + default: + break + } + } + + if manager.connection.status == .invalid { + manager.loadFromPreferences { (_) in + block() + } + } else { + block() + } + + return Promise.value(()) + } + } + + func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { + _ = refreshProviderManagers().then { () -> Promise in + let manager = self.providerManager(for: tunnel)! + let block = { + switch manager.connection.status { + case .connected, .connecting: + self.disconnect(tunnel: tunnel) + default: + break + } + } + + if manager.connection.status == .invalid { + manager.loadFromPreferences { (_) in + block() + } + } else { + block() + } + return Promise.value(()) + } + } + + private func connect(tunnel: Tunnel) { + os_log("connect tunnel: %{public}@", log: Log.general, type: .info, tunnel.description) + // Should the manager be enabled? + + let manager = providerManager(for: tunnel) + manager?.isEnabled = true + manager?.saveToPreferences { (error) in + if let error = error { + os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) + return + } + os_log("saved preferences", log: Log.general, type: .info) + + let session = manager?.connection as! NETunnelProviderSession //swiftlint:disable:this force_cast + do { + try session.startTunnel() + } catch let error { + os_log("error starting tunnel: %{public}@", log: Log.general, type: .error, error.localizedDescription) + } + } + } + + func disconnect(tunnel: Tunnel) { + let manager = providerManager(for: tunnel) + manager?.connection.stopVPNTunnel() + } + + func info(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { + print("info tunnel \(tunnel)") + + showTunnelInfoViewController(tunnel: tunnel, context: self.persistentContainer.viewContext) + } + + func delete(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { + print("delete tunnel \(tunnel)") + + if let moc = tunnel.managedObjectContext { + moc.perform { + moc.delete(tunnel) + moc.saveContextToStore() + } + let manager = providerManager(for: tunnel) + manager?.removeFromPreferences { (error) in + if let error = error { + os_log("error removing preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) + return + } + self.providerManagers?.removeAll { $0 == manager } + os_log("removed preferences", log: Log.general, type: .info) + } + } + } + + private func providerManager(for tunnel: Tunnel) -> NETunnelProviderManager? { + return self.providerManagers?.first { + guard let prot = $0.protocolConfiguration as? NETunnelProviderProtocol else { + return false + } + guard let tunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else { + return false + } + return tunnelIdentifier == tunnel.tunnelIdentifier + } + } + + func saveTunnel(_ tunnel: Tunnel) { + let manager = providerManager(for: tunnel) ?? NETunnelProviderManager() + manager.localizedDescription = tunnel.title + + let protocolConfiguration = NETunnelProviderProtocol() + protocolConfiguration.providerBundleIdentifier = VPNBUNDLE + protocolConfiguration.serverAddress = (tunnel.peers?.array as? [Peer])?.compactMap { $0.endpoint}.joined(separator: ", ") + protocolConfiguration.providerConfiguration = tunnel.generateProviderConfiguration() + + manager.protocolConfiguration = protocolConfiguration + let connectRule = NEOnDemandRuleConnect() + connectRule.interfaceTypeMatch = .any + manager.onDemandRules = [connectRule] + + manager.saveToPreferences { (error) in + if let error = error { + os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) + return + } + os_log("saved preferences", log: Log.general, type: .info) + } + + _ = refreshProviderManagers().then { () -> Promise in + self.navigationController.popViewController(animated: true) + return Promise.value(()) + } + } +} diff --git a/WireGuard/Coordinators/AppCoordinator.swift b/WireGuard/Coordinators/AppCoordinator.swift index d8cf5db..d94c45e 100644 --- a/WireGuard/Coordinators/AppCoordinator.swift +++ b/WireGuard/Coordinators/AppCoordinator.swift @@ -27,7 +27,7 @@ class AppCoordinator: RootViewCoordinator { let persistentContainer = NSPersistentContainer(name: "WireGuard") let storyboard = UIStoryboard(name: "Main", bundle: nil) var providerManagers: [NETunnelProviderManager]? - private let documentPickerDelegate: AppDocumentPickerDelegate + let documentPickerDelegateObject: AppDocumentPickerDelegate // MARK: - Properties @@ -54,8 +54,8 @@ class AppCoordinator: RootViewCoordinator { self.window.rootViewController = self.navigationController self.window.makeKeyAndVisible() - documentPickerDelegate = AppDocumentPickerDelegate() - documentPickerDelegate.appCoordinator = self + documentPickerDelegateObject = AppDocumentPickerDelegate() + documentPickerDelegateObject.appCoordinator = self NotificationCenter.default.addObserver(self, selector: #selector(VPNStatusDidChange(notification:)), @@ -315,227 +315,6 @@ class AppCoordinator: RootViewCoordinator { } } -extension AppCoordinator: TunnelInfoTableViewControllerDelegate { - func configure(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) { - print("configure tunnel \(tunnel)") - let editContext = persistentContainer.newBackgroundContext() - var backgroundTunnel: Tunnel? - editContext.performAndWait { - backgroundTunnel = editContext.object(with: tunnel.objectID) as? Tunnel - } - - showTunnelConfigurationViewController(tunnel: backgroundTunnel, context: editContext) - } - -} - -extension AppCoordinator: TunnelsTableViewControllerDelegate { - func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus { - let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession - return session?.status ?? .invalid - } - - func addProvider(tunnelsTableViewController: TunnelsTableViewController) { - let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Add from File", comment: ""), style: .default) { [unowned self] _ in - self.addProviderFromFile() - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Add Manually", comment: ""), style: .default) { [unowned self] _ in - self.addProviderManually() - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Scan QR Code", comment: ""), style: .default) { [unowned self] _ in - self.addProviderWithQRScan() - }) - actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel)) - - tunnelsTableViewController.present(actionSheet, animated: true, completion: nil) - } - - func addProviderFromFile() { - let documentPickerController = UIDocumentPickerViewController(documentTypes: [String(kUTTypeZipArchive), "com.wireguard.config.quick"], in: .import) - documentPickerController.delegate = documentPickerDelegate - tunnelsTableViewController.present(documentPickerController, animated: true, completion: nil) - } - - func addProviderManually() { - let addContext = persistentContainer.newBackgroundContext() - showTunnelConfigurationViewController(tunnel: nil, context: addContext) - } - - func addProviderWithQRScan() { - let addContext = persistentContainer.newBackgroundContext() - - let qrScanViewController = storyboard.instantiateViewController(type: QRScanViewController.self) - - qrScanViewController.configure(context: addContext, delegate: self) - - self.navigationController.pushViewController(qrScanViewController, animated: true) - } - - func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { - _ = refreshProviderManagers().then { () -> Promise in - let manager = self.providerManager(for: tunnel)! - let block = { - switch manager.connection.status { - case .invalid, .disconnected: - self.connect(tunnel: tunnel) - default: - break - } - } - - if manager.connection.status == .invalid { - manager.loadFromPreferences { (_) in - block() - } - } else { - block() - } - - return Promise.value(()) - } - } - - func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { - _ = refreshProviderManagers().then { () -> Promise in - let manager = self.providerManager(for: tunnel)! - let block = { - switch manager.connection.status { - case .connected, .connecting: - self.disconnect(tunnel: tunnel) - default: - break - } - } - - if manager.connection.status == .invalid { - manager.loadFromPreferences { (_) in - block() - } - } else { - block() - } - return Promise.value(()) - } - } - - private func connect(tunnel: Tunnel) { - os_log("connect tunnel: %{public}@", log: Log.general, type: .info, tunnel.description) - // Should the manager be enabled? - - let manager = providerManager(for: tunnel) - manager?.isEnabled = true - manager?.saveToPreferences { (error) in - if let error = error { - os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) - return - } - os_log("saved preferences", log: Log.general, type: .info) - - let session = manager?.connection as! NETunnelProviderSession //swiftlint:disable:this force_cast - do { - try session.startTunnel() - } catch let error { - os_log("error starting tunnel: %{public}@", log: Log.general, type: .error, error.localizedDescription) - } - } - } - - func disconnect(tunnel: Tunnel) { - let manager = providerManager(for: tunnel) - manager?.connection.stopVPNTunnel() - } - - func info(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { - print("info tunnel \(tunnel)") - - showTunnelInfoViewController(tunnel: tunnel, context: self.persistentContainer.viewContext) - } - - func delete(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { - print("delete tunnel \(tunnel)") - - if let moc = tunnel.managedObjectContext { - moc.perform { - moc.delete(tunnel) - moc.saveContextToStore() - } - let manager = providerManager(for: tunnel) - manager?.removeFromPreferences { (error) in - if let error = error { - os_log("error removing preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) - return - } - self.providerManagers?.removeAll { $0 == manager } - os_log("removed preferences", log: Log.general, type: .info) - } - } - } - - private func providerManager(for tunnel: Tunnel) -> NETunnelProviderManager? { - return self.providerManagers?.first { - guard let prot = $0.protocolConfiguration as? NETunnelProviderProtocol else { - return false - } - guard let tunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else { - return false - } - return tunnelIdentifier == tunnel.tunnelIdentifier - } - } - - private func saveTunnel(_ tunnel: Tunnel) { - let manager = providerManager(for: tunnel) ?? NETunnelProviderManager() - manager.localizedDescription = tunnel.title - - let protocolConfiguration = NETunnelProviderProtocol() - protocolConfiguration.providerBundleIdentifier = VPNBUNDLE - protocolConfiguration.serverAddress = (tunnel.peers?.array as? [Peer])?.compactMap { $0.endpoint}.joined(separator: ", ") - protocolConfiguration.providerConfiguration = tunnel.generateProviderConfiguration() - - manager.protocolConfiguration = protocolConfiguration - let connectRule = NEOnDemandRuleConnect() - connectRule.interfaceTypeMatch = .any - manager.onDemandRules = [connectRule] - - manager.saveToPreferences { (error) in - if let error = error { - os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) - return - } - os_log("saved preferences", log: Log.general, type: .info) - } - - _ = refreshProviderManagers().then { () -> Promise in - self.navigationController.popViewController(animated: true) - return Promise.value(()) - } - } -} - -extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate { - func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) { - saveTunnel(tunnel) - } -} - -extension AppCoordinator: QRScanViewControllerDelegate { - func didSave(tunnel: Tunnel, qrScanViewController: QRScanViewController) { - qrScanViewController.navigationController?.popViewController(animated: true) - showTunnelInfoViewController(tunnel: tunnel, context: tunnel.managedObjectContext!) - } - - func didCancel(qrScanViewController: QRScanViewController) { - qrScanViewController.navigationController?.popViewController(animated: true) - } -} - -extension AppCoordinator: SettingsTableViewControllerDelegate { - func exportTunnels(settingsTableViewController: SettingsTableViewController, sourceView: UIView) { - self.exportConfigs(sourceView: sourceView) - } -} - class AppDocumentPickerDelegate: NSObject, UIDocumentPickerDelegate { weak var appCoordinator: AppCoordinator?