Zip export.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
2572c4781c
commit
8766750bb8
1
Podfile
1
Podfile
|
@ -9,6 +9,7 @@ target 'WireGuard' do
|
|||
pod 'PromiseKit/CorePromise'
|
||||
pod 'Disk'
|
||||
pod 'BNRCoreDataStack'
|
||||
pod 'ZIPFoundation'
|
||||
|
||||
post_install do | installer |
|
||||
require 'fileutils'
|
||||
|
|
|
@ -3,12 +3,14 @@ PODS:
|
|||
- Disk (0.3.3)
|
||||
- PromiseKit/CorePromise (6.3.4)
|
||||
- SwiftLint (0.27.0)
|
||||
- ZIPFoundation (0.9.6)
|
||||
|
||||
DEPENDENCIES:
|
||||
- BNRCoreDataStack
|
||||
- Disk
|
||||
- PromiseKit/CorePromise
|
||||
- SwiftLint
|
||||
- ZIPFoundation
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
|
@ -16,13 +18,15 @@ SPEC REPOS:
|
|||
- Disk
|
||||
- PromiseKit
|
||||
- SwiftLint
|
||||
- ZIPFoundation
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
BNRCoreDataStack: d9d7d0ed1afd27dca5a903dde1250aa4c6dbc3f5
|
||||
Disk: d1f55cd61f6ca20f368232d0c6e37e3c3dfcb63e
|
||||
PromiseKit: e1425568123ec844a944c93f2bcb29d511d39cf5
|
||||
SwiftLint: 3207c1faa2240bf8973b191820a116113cd11073
|
||||
ZIPFoundation: 68c35eb2637c21abe56dc60b3277636443bc1501
|
||||
|
||||
PODFILE CHECKSUM: 82a0eed9b58c48f9be6c02c4e9d17a979a496c18
|
||||
PODFILE CHECKSUM: 5f7ab91f3becde6fa3b3b8842dc93ae44e92c61f
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
|
|
|
@ -134,6 +134,37 @@ SOFTWARE.
|
|||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>MIT License
|
||||
|
||||
Copyright (c) 2017 Thomas Zoechling
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>ZIPFoundation</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Generated by CocoaPods - https://cocoapods.org</string>
|
||||
|
|
|
@ -538,12 +538,14 @@
|
|||
"${BUILT_PRODUCTS_DIR}/BNRCoreDataStack/BNRCoreDataStack.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Disk/Disk.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BNRCoreDataStack.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Disk.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
|
|
|
@ -87,11 +87,18 @@
|
|||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="WireGuard Tunnels" largeTitleDisplayMode="always" id="j0L-5U-jDs">
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="add" id="h2H-H8-3Tn">
|
||||
<connections>
|
||||
<action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<rightBarButtonItems>
|
||||
<barButtonItem systemItem="add" id="h2H-H8-3Tn">
|
||||
<connections>
|
||||
<action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem systemItem="action" id="zxB-ao-5JR">
|
||||
<connections>
|
||||
<action selector="exportTunnels:" destination="kTU-BV-32R" id="PwM-I1-n6B"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
</tableViewController>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import Foundation
|
||||
import NetworkExtension
|
||||
import os.log
|
||||
import ZIPFoundation
|
||||
|
||||
import CoreData
|
||||
import BNRCoreDataStack
|
||||
|
@ -111,6 +112,72 @@ class AppCoordinator: RootViewCoordinator {
|
|||
|
||||
}
|
||||
|
||||
// swiftlint:disable next function_body_length
|
||||
func exportConfigs(barButtonItem: UIBarButtonItem) {
|
||||
guard let path = FileManager.default
|
||||
.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
||||
return
|
||||
}
|
||||
let saveFileURL = path.appendingPathComponent("wireguard-export.zip")
|
||||
do {
|
||||
try FileManager.default.removeItem(at: saveFileURL)
|
||||
} catch {
|
||||
os_log("Failed to delete file: %{public}@ : %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString, error.localizedDescription)
|
||||
}
|
||||
|
||||
guard let archive = Archive(url: saveFileURL, accessMode: .create) else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
var tunnelsByTitle = [String: [Tunnel]]()
|
||||
let tunnels = try Tunnel.allInContext(persistentContainer.viewContext)
|
||||
tunnels.forEach {
|
||||
guard let title = $0.title ?? $0.tunnelIdentifier else {
|
||||
// there is always a tunnelidentifier.
|
||||
return
|
||||
}
|
||||
if let tunnels = tunnelsByTitle[title] {
|
||||
tunnelsByTitle[title] = tunnels + [$0]
|
||||
} else {
|
||||
tunnelsByTitle[title] = [$0]
|
||||
}
|
||||
}
|
||||
|
||||
func addEntry(title: String, tunnel: Tunnel) throws {
|
||||
let data = tunnel.export().data(using: .utf8)!
|
||||
let byteCount: UInt32 = UInt32(data.count)
|
||||
try archive.addEntry(with: "\(title).conf", type: .file, uncompressedSize: byteCount, provider: { (position, size) -> Data in
|
||||
return data.subdata(in: position ..< size)
|
||||
})
|
||||
}
|
||||
|
||||
try tunnelsByTitle.keys.forEach {
|
||||
if let tunnels = tunnelsByTitle[$0] {
|
||||
if tunnels.count == 1 {
|
||||
try addEntry(title: $0, tunnel: tunnels[0])
|
||||
} else {
|
||||
for (index, tunnel) in tunnels.enumerated() {
|
||||
try addEntry(title: $0 + "-\(index + 1)", tunnel: tunnel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
os_log("Failed to create archive file: %{public}@ : %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString, error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
let activityViewController = UIActivityViewController(
|
||||
activityItems: [saveFileURL],
|
||||
applicationActivities: nil)
|
||||
if let popoverPresentationController = activityViewController.popoverPresentationController {
|
||||
popoverPresentationController.barButtonItem = barButtonItem
|
||||
}
|
||||
navigationController.present(activityViewController, animated: true) {
|
||||
}
|
||||
}
|
||||
|
||||
func exportConfig(tunnel: Tunnel, barButtonItem: UIBarButtonItem) {
|
||||
let exportString = tunnel.export()
|
||||
|
||||
|
@ -195,6 +262,10 @@ class AppCoordinator: RootViewCoordinator {
|
|||
}
|
||||
|
||||
extension AppCoordinator: TunnelsTableViewControllerDelegate {
|
||||
func exportTunnels(tunnelsTableViewController: TunnelsTableViewController, barButtonItem: UIBarButtonItem) {
|
||||
self.exportConfigs(barButtonItem: barButtonItem)
|
||||
}
|
||||
|
||||
func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus {
|
||||
let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession
|
||||
return session?.status ?? .invalid
|
||||
|
|
|
@ -13,6 +13,7 @@ import BNRCoreDataStack
|
|||
import NetworkExtension
|
||||
|
||||
protocol TunnelsTableViewControllerDelegate: class {
|
||||
func exportTunnels(tunnelsTableViewController: TunnelsTableViewController, barButtonItem: UIBarButtonItem)
|
||||
func addProvider(tunnelsTableViewController: TunnelsTableViewController)
|
||||
func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
||||
func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
||||
|
@ -63,7 +64,11 @@ class TunnelsTableViewController: UITableViewController {
|
|||
tableView.tableFooterView = UIView(frame: CGRect.zero)
|
||||
}
|
||||
|
||||
@IBAction func addProvider(_ sender: Any) {
|
||||
@IBAction func exportTunnels(_ sender: UIBarButtonItem) {
|
||||
delegate?.exportTunnels(tunnelsTableViewController: self, barButtonItem: sender)
|
||||
}
|
||||
|
||||
@IBAction func addProvider(_ sender: UIBarButtonItem) {
|
||||
delegate?.addProvider(tunnelsTableViewController: self)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue