Zip export.
This commit is contained in:
parent
815f12c9a3
commit
ca35168dc9
1
Podfile
1
Podfile
|
@ -9,6 +9,7 @@ target 'WireGuard' do
|
||||||
pod 'PromiseKit/CorePromise'
|
pod 'PromiseKit/CorePromise'
|
||||||
pod 'Disk'
|
pod 'Disk'
|
||||||
pod 'BNRCoreDataStack'
|
pod 'BNRCoreDataStack'
|
||||||
|
pod 'ZIPFoundation'
|
||||||
|
|
||||||
post_install do | installer |
|
post_install do | installer |
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
|
|
|
@ -3,12 +3,14 @@ PODS:
|
||||||
- Disk (0.3.3)
|
- Disk (0.3.3)
|
||||||
- PromiseKit/CorePromise (6.3.4)
|
- PromiseKit/CorePromise (6.3.4)
|
||||||
- SwiftLint (0.27.0)
|
- SwiftLint (0.27.0)
|
||||||
|
- ZIPFoundation (0.9.6)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- BNRCoreDataStack
|
- BNRCoreDataStack
|
||||||
- Disk
|
- Disk
|
||||||
- PromiseKit/CorePromise
|
- PromiseKit/CorePromise
|
||||||
- SwiftLint
|
- SwiftLint
|
||||||
|
- ZIPFoundation
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://github.com/cocoapods/specs.git:
|
https://github.com/cocoapods/specs.git:
|
||||||
|
@ -16,13 +18,15 @@ SPEC REPOS:
|
||||||
- Disk
|
- Disk
|
||||||
- PromiseKit
|
- PromiseKit
|
||||||
- SwiftLint
|
- SwiftLint
|
||||||
|
- ZIPFoundation
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
BNRCoreDataStack: d9d7d0ed1afd27dca5a903dde1250aa4c6dbc3f5
|
BNRCoreDataStack: d9d7d0ed1afd27dca5a903dde1250aa4c6dbc3f5
|
||||||
Disk: d1f55cd61f6ca20f368232d0c6e37e3c3dfcb63e
|
Disk: d1f55cd61f6ca20f368232d0c6e37e3c3dfcb63e
|
||||||
PromiseKit: e1425568123ec844a944c93f2bcb29d511d39cf5
|
PromiseKit: e1425568123ec844a944c93f2bcb29d511d39cf5
|
||||||
SwiftLint: 3207c1faa2240bf8973b191820a116113cd11073
|
SwiftLint: 3207c1faa2240bf8973b191820a116113cd11073
|
||||||
|
ZIPFoundation: 68c35eb2637c21abe56dc60b3277636443bc1501
|
||||||
|
|
||||||
PODFILE CHECKSUM: 82a0eed9b58c48f9be6c02c4e9d17a979a496c18
|
PODFILE CHECKSUM: 5f7ab91f3becde6fa3b3b8842dc93ae44e92c61f
|
||||||
|
|
||||||
COCOAPODS: 1.5.3
|
COCOAPODS: 1.5.3
|
||||||
|
|
|
@ -134,6 +134,37 @@ SOFTWARE.
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSGroupSpecifier</string>
|
<string>PSGroupSpecifier</string>
|
||||||
</dict>
|
</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>
|
<dict>
|
||||||
<key>FooterText</key>
|
<key>FooterText</key>
|
||||||
<string>Generated by CocoaPods - https://cocoapods.org</string>
|
<string>Generated by CocoaPods - https://cocoapods.org</string>
|
||||||
|
|
|
@ -538,12 +538,14 @@
|
||||||
"${BUILT_PRODUCTS_DIR}/BNRCoreDataStack/BNRCoreDataStack.framework",
|
"${BUILT_PRODUCTS_DIR}/BNRCoreDataStack/BNRCoreDataStack.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/Disk/Disk.framework",
|
"${BUILT_PRODUCTS_DIR}/Disk/Disk.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework",
|
"${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework",
|
||||||
|
"${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework",
|
||||||
);
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BNRCoreDataStack.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BNRCoreDataStack.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Disk.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Disk.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework",
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
|
|
|
@ -87,11 +87,18 @@
|
||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<navigationItem key="navigationItem" title="WireGuard Tunnels" largeTitleDisplayMode="always" 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">
|
<rightBarButtonItems>
|
||||||
<connections>
|
<barButtonItem systemItem="add" id="h2H-H8-3Tn">
|
||||||
<action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/>
|
<connections>
|
||||||
</connections>
|
<action selector="addProvider:" destination="kTU-BV-32R" id="xSg-ap-3Fx"/>
|
||||||
</barButtonItem>
|
</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>
|
</navigationItem>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import os.log
|
import os.log
|
||||||
|
import ZIPFoundation
|
||||||
|
|
||||||
import CoreData
|
import CoreData
|
||||||
import BNRCoreDataStack
|
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) {
|
func exportConfig(tunnel: Tunnel, barButtonItem: UIBarButtonItem) {
|
||||||
let exportString = tunnel.export()
|
let exportString = tunnel.export()
|
||||||
|
|
||||||
|
@ -195,6 +262,10 @@ class AppCoordinator: RootViewCoordinator {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppCoordinator: TunnelsTableViewControllerDelegate {
|
extension AppCoordinator: TunnelsTableViewControllerDelegate {
|
||||||
|
func exportTunnels(tunnelsTableViewController: TunnelsTableViewController, barButtonItem: UIBarButtonItem) {
|
||||||
|
self.exportConfigs(barButtonItem: barButtonItem)
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -13,6 +13,7 @@ import BNRCoreDataStack
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
protocol TunnelsTableViewControllerDelegate: class {
|
protocol TunnelsTableViewControllerDelegate: class {
|
||||||
|
func exportTunnels(tunnelsTableViewController: TunnelsTableViewController, barButtonItem: UIBarButtonItem)
|
||||||
func addProvider(tunnelsTableViewController: TunnelsTableViewController)
|
func addProvider(tunnelsTableViewController: TunnelsTableViewController)
|
||||||
func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
||||||
func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
|
||||||
|
@ -63,7 +64,11 @@ class TunnelsTableViewController: UITableViewController {
|
||||||
tableView.tableFooterView = UIView(frame: CGRect.zero)
|
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)
|
delegate?.addProvider(tunnelsTableViewController: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue