Add import and export logic.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jeroen Leenarts 2018-08-31 23:06:40 +02:00
parent 4950254013
commit 2572c4781c
9 changed files with 178 additions and 10 deletions

BIN
Resources/icon_20pt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

BIN
Resources/icon_20pt@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -8,6 +8,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
48CF751B34E9703133F1B1AF /* Pods_WireGuard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 861983CAE8FDC13BC83E7E04 /* Pods_WireGuard.framework */; }; 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 */; }; 4A4351592124956200261999 /* Validators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4351582124956200261999 /* Validators.swift */; };
4A43515A2124956200261999 /* 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 */; }; 4A43515C21249E5700261999 /* ValidatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A43515B21249E5700261999 /* ValidatorsTests.swift */; };
@ -85,6 +87,9 @@
/* Begin PBXFileReference section */ /* 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 = "<group>"; }; 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 = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
4A430E7F2139DC8F0078172C /* icon_20pt@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_20pt@3x.png"; sourceTree = "<group>"; };
4A430E812139DCCB0078172C /* icon_20pt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_20pt.png; sourceTree = "<group>"; };
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 = "<group>"; }; 4A4351582124956200261999 /* Validators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Validators.swift; sourceTree = "<group>"; };
4A43515B21249E5700261999 /* ValidatorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorsTests.swift; sourceTree = "<group>"; }; 4A43515B21249E5700261999 /* ValidatorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorsTests.swift; sourceTree = "<group>"; };
4A4BA6D720B73CBA00223AB8 /* TunnelConfigurationTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelConfigurationTableViewController.swift; sourceTree = "<group>"; }; 4A4BA6D720B73CBA00223AB8 /* TunnelConfigurationTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelConfigurationTableViewController.swift; sourceTree = "<group>"; };
@ -223,6 +228,9 @@
4A4BAD0720B5F4BC00F12B28 /* Resources */ = { 4A4BAD0720B5F4BC00F12B28 /* Resources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4A430E7F2139DC8F0078172C /* icon_20pt@3x.png */,
4A430E812139DCCB0078172C /* icon_20pt.png */,
4A430E832139DCFB0078172C /* icon_60pt@3x.png */,
4A4BAD0520B5F4B500F12B28 /* Settings.bundle */, 4A4BAD0520B5F4B500F12B28 /* Settings.bundle */,
); );
path = Resources; path = Resources;
@ -468,6 +476,8 @@
files = ( files = (
4A4BAD0620B5F4B500F12B28 /* Settings.bundle in Resources */, 4A4BAD0620B5F4B500F12B28 /* Settings.bundle in Resources */,
4A4BACF020B5F1C100F12B28 /* LaunchScreen.storyboard 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 */, 4A4BACED20B5F1C100F12B28 /* Assets.xcassets in Resources */,
4A4BACEB20B5F1BF00F12B28 /* Main.storyboard in Resources */, 4A4BACEB20B5F1BF00F12B28 /* Main.storyboard in Resources */,
); );

View File

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import os.log
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -22,4 +23,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true 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
}
} }

View File

@ -86,7 +86,7 @@
<outlet property="delegate" destination="kTU-BV-32R" id="b6T-ZR-cmO"/> <outlet property="delegate" destination="kTU-BV-32R" id="b6T-ZR-cmO"/>
</connections> </connections>
</tableView> </tableView>
<navigationItem key="navigationItem" title="WireGuard Tunnels" id="j0L-5U-jDs"> <navigationItem key="navigationItem" title="WireGuard Tunnels" largeTitleDisplayMode="always" id="j0L-5U-jDs">
<barButtonItem key="rightBarButtonItem" systemItem="add" id="h2H-H8-3Tn"> <barButtonItem key="rightBarButtonItem" systemItem="add" id="h2H-H8-3Tn">
<connections> <connections>
<action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/> <action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/>
@ -486,14 +486,22 @@
</connections> </connections>
</tableView> </tableView>
<navigationItem key="navigationItem" title="Tunnel settings" id="PPu-rX-T38"> <navigationItem key="navigationItem" title="Tunnel settings" id="PPu-rX-T38">
<barButtonItem key="rightBarButtonItem" systemItem="save" id="mft-1l-bWa"> <rightBarButtonItems>
<barButtonItem systemItem="save" id="mft-1l-bWa">
<connections> <connections>
<action selector="saveTunnelConfiguration:" destination="0VM-73-EPX" id="Hni-0w-hFF"/> <action selector="saveTunnelConfiguration:" destination="0VM-73-EPX" id="Hni-0w-hFF"/>
</connections> </connections>
</barButtonItem> </barButtonItem>
<barButtonItem systemItem="action" id="uim-Mz-omJ">
<connections>
<action selector="exportTunnel:" destination="0VM-73-EPX" id="oVI-TL-4o9"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem> </navigationItem>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/> <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
<connections> <connections>
<outlet property="exportButton" destination="uim-Mz-omJ" id="1Tq-ie-xo7"/>
<outlet property="saveButton" destination="mft-1l-bWa" id="Uif-KZ-isx"/> <outlet property="saveButton" destination="mft-1l-bWa" id="Uif-KZ-isx"/>
</connections> </connections>
</tableViewController> </tableViewController>
@ -524,7 +532,7 @@
</constraints> </constraints>
<viewLayoutGuide key="safeArea" id="Soo-c9-MsX"/> <viewLayoutGuide key="safeArea" id="Soo-c9-MsX"/>
</view> </view>
<navigationItem key="navigationItem" title="Scan Code" largeTitleDisplayMode="never" id="WGY-tY-ySz"/> <navigationItem key="navigationItem" title="Scan Code" largeTitleDisplayMode="always" id="WGY-tY-ySz"/>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/> <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TQ2-zp-o40" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="TQ2-zp-o40" userLabel="First Responder" sceneMemberID="firstResponder"/>

View File

@ -13,6 +13,10 @@ import os.log
import CoreData import CoreData
import BNRCoreDataStack import BNRCoreDataStack
enum AppCoordinatorError: Error {
case configImportError(msg: String)
}
extension UINavigationController: Identifyable {} extension UINavigationController: Identifyable {}
let APPGROUP = "group.com.wireguard.ios.WireGuard" 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 // MARK: - NEVPNManager handling
@objc private func VPNStatusDidChange(notification: NSNotification) { @objc private func VPNStatusDidChange(notification: NSNotification) {
@ -151,7 +195,6 @@ class AppCoordinator: RootViewCoordinator {
} }
extension AppCoordinator: TunnelsTableViewControllerDelegate { extension AppCoordinator: TunnelsTableViewControllerDelegate {
func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus { func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus {
let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession
return session?.status ?? .invalid return session?.status ?? .invalid
@ -330,6 +373,10 @@ extension AppCoordinator: TunnelsTableViewControllerDelegate {
} }
extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate { extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate {
func export(tunnel: Tunnel, barButtonItem: UIBarButtonItem) {
exportConfig(tunnel: tunnel, barButtonItem: barButtonItem)
}
func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) { func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) {
saveTunnel(tunnel) saveTunnel(tunnel)
} }

View File

@ -2,10 +2,35 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSCameraUsageDescription</key> <key>CFBundleDisplayName</key>
<string>Camera is used to scan QR codes</string> <string>${PRODUCT_NAME}</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string></string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>icon_20pt</string>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>CFBundleTypeName</key>
<string>WireGuard configuration</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.wireguard.config.quick</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -22,6 +47,12 @@
<string>auto-generated</string> <string>auto-generated</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
<key>UIFileSharingEnabled</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Camera is used to scan QR codes</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
@ -45,5 +76,51 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>conf</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard configuration</string>
<key>UTTypeIconFiles</key>
<array>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.wireguard.config.quick</string>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>conf</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard configuration</string>
<key>UTTypeIconFiles</key>
<array>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.wireguard.config.quick</string>
</dict>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -13,11 +13,13 @@ import PromiseKit
protocol TunnelConfigurationTableViewControllerDelegate: class { protocol TunnelConfigurationTableViewControllerDelegate: class {
func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController)
func export(tunnel: Tunnel, barButtonItem: UIBarButtonItem)
} }
class TunnelConfigurationTableViewController: UITableViewController { class TunnelConfigurationTableViewController: UITableViewController {
@IBOutlet weak var saveButton: UIBarButtonItem! @IBOutlet weak var saveButton: UIBarButtonItem!
@IBOutlet weak var exportButton: UIBarButtonItem!
private var viewContext: NSManagedObjectContext! private var viewContext: NSManagedObjectContext!
private weak var delegate: TunnelConfigurationTableViewControllerDelegate? 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 { override func numberOfSections(in tableView: UITableView) -> Int {
return 3 return 3
} }

BIN
icon_20pt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B