diff --git a/Resources/icon_20pt.png b/Resources/icon_20pt.png new file mode 100644 index 0000000..78f0792 Binary files /dev/null and b/Resources/icon_20pt.png differ diff --git a/Resources/icon_20pt@3x.png b/Resources/icon_20pt@3x.png new file mode 100644 index 0000000..f7b6c66 Binary files /dev/null and b/Resources/icon_20pt@3x.png differ diff --git a/WireGuard.xcodeproj/project.pbxproj b/WireGuard.xcodeproj/project.pbxproj index c2c2713..fead2ea 100644 --- a/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 48CF751B34E9703133F1B1AF /* Pods_WireGuard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 861983CAE8FDC13BC83E7E04 /* Pods_WireGuard.framework */; }; + 4A430E802139DC8F0078172C /* icon_20pt@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4A430E7F2139DC8F0078172C /* icon_20pt@3x.png */; }; + 4A430E842139DCFB0078172C /* icon_60pt@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4A430E832139DCFB0078172C /* icon_60pt@3x.png */; }; 4A4351592124956200261999 /* Validators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4351582124956200261999 /* Validators.swift */; }; 4A43515A2124956200261999 /* Validators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4351582124956200261999 /* Validators.swift */; }; 4A43515C21249E5700261999 /* ValidatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A43515B21249E5700261999 /* ValidatorsTests.swift */; }; @@ -85,6 +87,9 @@ /* Begin PBXFileReference section */ 0CE52E030FAA93F3BF5747B2 /* Pods-WireGuard.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WireGuard.release.xcconfig"; path = "Pods/Target Support Files/Pods-WireGuard/Pods-WireGuard.release.xcconfig"; sourceTree = ""; }; 25E2BE31A33C8CCE6E79B6EF /* Pods-WireGuard.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WireGuard.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WireGuard/Pods-WireGuard.debug.xcconfig"; sourceTree = ""; }; + 4A430E7F2139DC8F0078172C /* icon_20pt@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_20pt@3x.png"; sourceTree = ""; }; + 4A430E812139DCCB0078172C /* icon_20pt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_20pt.png; sourceTree = ""; }; + 4A430E832139DCFB0078172C /* icon_60pt@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon_60pt@3x.png"; path = "WireGuard/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png"; sourceTree = SOURCE_ROOT; }; 4A4351582124956200261999 /* Validators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Validators.swift; sourceTree = ""; }; 4A43515B21249E5700261999 /* ValidatorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorsTests.swift; sourceTree = ""; }; 4A4BA6D720B73CBA00223AB8 /* TunnelConfigurationTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelConfigurationTableViewController.swift; sourceTree = ""; }; @@ -223,6 +228,9 @@ 4A4BAD0720B5F4BC00F12B28 /* Resources */ = { isa = PBXGroup; children = ( + 4A430E7F2139DC8F0078172C /* icon_20pt@3x.png */, + 4A430E812139DCCB0078172C /* icon_20pt.png */, + 4A430E832139DCFB0078172C /* icon_60pt@3x.png */, 4A4BAD0520B5F4B500F12B28 /* Settings.bundle */, ); path = Resources; @@ -468,6 +476,8 @@ files = ( 4A4BAD0620B5F4B500F12B28 /* Settings.bundle in Resources */, 4A4BACF020B5F1C100F12B28 /* LaunchScreen.storyboard in Resources */, + 4A430E842139DCFB0078172C /* icon_60pt@3x.png in Resources */, + 4A430E802139DC8F0078172C /* icon_20pt@3x.png in Resources */, 4A4BACED20B5F1C100F12B28 /* Assets.xcassets in Resources */, 4A4BACEB20B5F1BF00F12B28 /* Main.storyboard in Resources */, ); diff --git a/WireGuard/AppDelegate.swift b/WireGuard/AppDelegate.swift index e4b8f1e..2460a9f 100644 --- a/WireGuard/AppDelegate.swift +++ b/WireGuard/AppDelegate.swift @@ -7,6 +7,7 @@ // import UIKit +import os.log @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -22,4 +23,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + + func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool { + defer { + do { + try FileManager.default.removeItem(at: url) + } catch { + os_log("Failed to remove item from Inbox: %{public}@", log: Log.general, type: .error, url.absoluteString) + } + } + guard url.pathExtension == "conf" else { return false } + + do { + try appCoordinator.importConfig(config: url) + } catch { + os_log("Unable to import config: %{public}@", log: Log.general, type: .error, url.absoluteString) + return false + } + return true + } } diff --git a/WireGuard/Base.lproj/Main.storyboard b/WireGuard/Base.lproj/Main.storyboard index 42a8dfe..ccf19f2 100644 --- a/WireGuard/Base.lproj/Main.storyboard +++ b/WireGuard/Base.lproj/Main.storyboard @@ -86,7 +86,7 @@ - + @@ -486,14 +486,22 @@ - - - - - + + + + + + + + + + + + + @@ -524,7 +532,7 @@ - + diff --git a/WireGuard/Coordinators/AppCoordinator.swift b/WireGuard/Coordinators/AppCoordinator.swift index bf42aa8..5bb83e2 100644 --- a/WireGuard/Coordinators/AppCoordinator.swift +++ b/WireGuard/Coordinators/AppCoordinator.swift @@ -13,6 +13,10 @@ import os.log import CoreData import BNRCoreDataStack +enum AppCoordinatorError: Error { + case configImportError(msg: String) +} + extension UINavigationController: Identifyable {} let APPGROUP = "group.com.wireguard.ios.WireGuard" @@ -92,6 +96,46 @@ class AppCoordinator: RootViewCoordinator { } } + func importConfig(config: URL) throws { + do { + let configString = try String(contentsOf: config) + let addContext = persistentContainer.newBackgroundContext() + let tunnel = try Tunnel.fromConfig(configString, context: addContext) + let title = config.deletingPathExtension().lastPathComponent + tunnel.title = title + addContext.saveContext() + self.saveTunnel(tunnel) + } catch { + throw AppCoordinatorError.configImportError(msg: "Failed") + } + + } + + func exportConfig(tunnel: Tunnel, barButtonItem: UIBarButtonItem) { + let exportString = tunnel.export() + + guard let path = FileManager.default + .urls(for: .documentDirectory, in: .userDomainMask).first else { + return + } + let saveFileURL = path.appendingPathComponent("/\(tunnel.title ?? "wireguard").conf") + do { + try exportString.write(to: saveFileURL, atomically: true, encoding: .utf8) + } catch { + os_log("Failed to export tunnelto: %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString) + return + } + + let activityViewController = UIActivityViewController( + activityItems: [saveFileURL], + applicationActivities: nil) + if let popoverPresentationController = activityViewController.popoverPresentationController { + popoverPresentationController.barButtonItem = barButtonItem + } + self.navigationController.present(activityViewController, animated: true) { + } + } + // MARK: - NEVPNManager handling @objc private func VPNStatusDidChange(notification: NSNotification) { @@ -151,7 +195,6 @@ class AppCoordinator: RootViewCoordinator { } extension AppCoordinator: TunnelsTableViewControllerDelegate { - func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus { let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession return session?.status ?? .invalid @@ -330,6 +373,10 @@ extension AppCoordinator: TunnelsTableViewControllerDelegate { } extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate { + func export(tunnel: Tunnel, barButtonItem: UIBarButtonItem) { + exportConfig(tunnel: tunnel, barButtonItem: barButtonItem) + } + func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) { saveTunnel(tunnel) } diff --git a/WireGuard/Info.plist b/WireGuard/Info.plist index f3073e3..f3d505d 100644 --- a/WireGuard/Info.plist +++ b/WireGuard/Info.plist @@ -2,10 +2,35 @@ - NSCameraUsageDescription - Camera is used to scan QR codes + CFBundleDisplayName + ${PRODUCT_NAME} CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + + + CFBundleTypeIconFiles + + icon_20pt + icon_20pt@3x + icon_60pt@3x + + CFBundleTypeName + WireGuard configuration + CFBundleTypeRole + Viewer + LSHandlerRank + Owner + LSItemContentTypes + + com.wireguard.config.quick + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -22,6 +47,12 @@ auto-generated LSRequiresIPhoneOS + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + NSCameraUsageDescription + Camera is used to scan QR codes UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -45,5 +76,51 @@ UIViewControllerBasedStatusBarAppearance + UTExportedTypeDeclarations + + + UTTypeTagSpecification + + public.filename-extension + conf + + UTTypeConformsTo + + public.data + + UTTypeDescription + WireGuard configuration + UTTypeIconFiles + + icon_20pt@3x + icon_60pt@3x + + UTTypeIdentifier + com.wireguard.config.quick + + + UTImportedTypeDeclarations + + + UTTypeTagSpecification + + public.filename-extension + conf + + UTTypeConformsTo + + public.data + + UTTypeDescription + WireGuard configuration + UTTypeIconFiles + + icon_20pt@3x + icon_60pt@3x + + UTTypeIdentifier + com.wireguard.config.quick + + diff --git a/WireGuard/ViewControllers/TunnelConfigurationTableViewController.swift b/WireGuard/ViewControllers/TunnelConfigurationTableViewController.swift index 77141fe..9a4a7e5 100644 --- a/WireGuard/ViewControllers/TunnelConfigurationTableViewController.swift +++ b/WireGuard/ViewControllers/TunnelConfigurationTableViewController.swift @@ -13,11 +13,13 @@ import PromiseKit protocol TunnelConfigurationTableViewControllerDelegate: class { func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) + func export(tunnel: Tunnel, barButtonItem: UIBarButtonItem) } class TunnelConfigurationTableViewController: UITableViewController { @IBOutlet weak var saveButton: UIBarButtonItem! + @IBOutlet weak var exportButton: UIBarButtonItem! private var viewContext: NSManagedObjectContext! private weak var delegate: TunnelConfigurationTableViewControllerDelegate? @@ -61,6 +63,10 @@ class TunnelConfigurationTableViewController: UITableViewController { } } + @IBAction func exportTunnel(_ sender: UIBarButtonItem) { + self.delegate?.export(tunnel: tunnel, barButtonItem: sender) + } + override func numberOfSections(in tableView: UITableView) -> Int { return 3 } diff --git a/icon_20pt.png b/icon_20pt.png new file mode 100644 index 0000000..78f0792 Binary files /dev/null and b/icon_20pt.png differ