Export: Exporting config files

This commit is contained in:
Roopesh Chander 2018-10-29 22:56:25 +05:30
parent d63ee03d66
commit 22dd3dcc40
5 changed files with 138 additions and 2 deletions

View File

@ -34,6 +34,7 @@
6FDEF80021863C0100D8FBF6 /* ioapi.c in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF7FF21863C0100D8FBF6 /* ioapi.c */; };
6FDEF802218646BA00D8FBF6 /* ZipArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF801218646B900D8FBF6 /* ZipArchive.swift */; };
6FDEF806218725D200D8FBF6 /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF805218725D200D8FBF6 /* SettingsTableViewController.swift */; };
6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDEF8072187442100D8FBF6 /* WgQuickConfigFileWriter.swift */; };
6FF4AC1F211EC472002C96EB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC1E211EC472002C96EB /* Assets.xcassets */; };
6FF4AC22211EC472002C96EB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC20211EC472002C96EB /* LaunchScreen.storyboard */; };
6FF4AC472120B9E0002C96EB /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FF4AC462120B9E0002C96EB /* NetworkExtension.framework */; };
@ -105,6 +106,7 @@
6FDEF7FF21863C0100D8FBF6 /* ioapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ioapi.c; sourceTree = "<group>"; };
6FDEF801218646B900D8FBF6 /* ZipArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipArchive.swift; sourceTree = "<group>"; };
6FDEF805218725D200D8FBF6 /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = "<group>"; };
6FDEF8072187442100D8FBF6 /* WgQuickConfigFileWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WgQuickConfigFileWriter.swift; sourceTree = "<group>"; };
6FF4AC14211EC46F002C96EB /* WireGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
6FF4AC1E211EC472002C96EB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6FF4AC21211EC472002C96EB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@ -167,6 +169,7 @@
isa = PBXGroup;
children = (
6F6899AB218099F00012E523 /* WgQuickConfigFileParser.swift */,
6FDEF8072187442100D8FBF6 /* WgQuickConfigFileWriter.swift */,
);
path = ConfigFile;
sourceTree = "<group>";
@ -445,6 +448,7 @@
6FDEF7FC21863B6100D8FBF6 /* zip.c in Sources */,
6F628C3F217F3413003482A3 /* DNSServer.swift in Sources */,
6F628C3D217F09E9003482A3 /* TunnelViewModel.swift in Sources */,
6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */,
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
6F7774E82172020C006A79B3 /* Configuration.swift in Sources */,
6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */,

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All rights reserved.
import UIKit
class WgQuickConfigFileWriter {
static func writeConfigFile(from tc: TunnelConfiguration) -> Data? {
let interface = tc.interface
var output = "[Interface]\n"
output.append("PrivateKey=\(interface.privateKey.base64EncodedString())\n")
if let listenPort = interface.listenPort {
output.append("ListenPort=\(listenPort)\n")
}
if (!interface.addresses.isEmpty) {
let addressString = interface.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("Address=\(addressString)\n")
}
if (!interface.dns.isEmpty) {
let dnsString = interface.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("DNS=\(dnsString)\n")
}
if let mtu = interface.mtu {
output.append("MTU=\(mtu)\n")
}
output.append("\n")
for peer in tc.peers {
output.append("[Peers]\n")
output.append("PublicKey=\(peer.publicKey.base64EncodedString())\n")
if let preSharedKey = peer.preSharedKey {
output.append("PresharedKey=\(preSharedKey.base64EncodedString())\n")
}
if (!peer.allowedIPs.isEmpty) {
let allowedIPsString = peer.allowedIPs.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("AllowedIPs=\(allowedIPsString)\n")
}
if let endpoint = peer.endpoint {
output.append("Endpoint=\(endpoint.stringRepresentation())\n")
}
if let persistentKeepAlive = peer.persistentKeepAlive {
output.append("PersistentKeepalive=\(persistentKeepAlive)\n")
}
output.append("\n")
}
return output.data(using: .utf8)
}
}

View File

@ -2,6 +2,7 @@
// Copyright © 2018 WireGuard LLC. All rights reserved.
import UIKit
import os.log
class SettingsTableViewController: UITableViewController {
@ -16,7 +17,10 @@ class SettingsTableViewController: UITableViewController {
[.exportZipArchive]
]
init() {
let tunnelsManager: TunnelsManager?
init(tunnelsManager: TunnelsManager?) {
self.tunnelsManager = tunnelsManager
super.init(style: .grouped)
}
@ -39,6 +43,65 @@ class SettingsTableViewController: UITableViewController {
@objc func doneTapped() {
dismiss(animated: true, completion: nil)
}
func exportConfigurationsAsZipFile(sourceView: UIView) {
guard let tunnelsManager = tunnelsManager, tunnelsManager.numberOfTunnels() > 0 else {
showErrorAlert(title: "Nothing to export", message: "There are no tunnel configurations to export")
return
}
var inputsToArchiver: [(fileName: String, contents: Data)] = []
var usedNames: Set<String> = []
for i in 0 ..< tunnelsManager.numberOfTunnels() {
guard let tunnelConfiguration = tunnelsManager.tunnel(at: i).tunnelConfiguration() else { continue }
if let contents = WgQuickConfigFileWriter.writeConfigFile(from: tunnelConfiguration) {
let name = tunnelConfiguration.interface.name
var nameToCheck = name
var i = 0
while (usedNames.contains(nameToCheck)) {
i = i + 1
nameToCheck = "\(name)\(i)"
}
usedNames.insert(nameToCheck)
inputsToArchiver.append((fileName: "\(nameToCheck).conf", contents: contents))
}
}
// Based on file export code by Jeroen Leenarts <jeroen.leenarts@gmail.com> in commit ca35168
guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let destinationURL = destinationDir.appendingPathComponent("wireguard-export.zip")
do {
try FileManager.default.removeItem(at: destinationURL)
} catch {
os_log("Failed to delete file: %{public}@ : %{public}@", log: OSLog.default, type: .error, destinationURL.absoluteString, error.localizedDescription)
}
var ok = false
do {
try ZipArchive.archive(inputs: inputsToArchiver, to: destinationURL)
ok = true
} catch {
os_log("Failed to create archive: %{public}@ : %{public}@", log: OSLog.default, type: .error, destinationURL.absoluteString)
}
if (ok) {
let activityVC = UIActivityViewController(activityItems: [destinationURL], applicationActivities: nil)
// popoverPresentationController shall be non-nil on the iPad
activityVC.popoverPresentationController?.sourceView = sourceView
present(activityVC, animated: true)
} else {
showErrorAlert(title: "Could not export", message: "There was an error creating the tunnel configuration archive")
}
}
func showErrorAlert(title: String, message: String) {
let okAction = UIAlertAction(title: "Ok", style: .default)
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
}
// MARK: UITableViewDataSource
@ -79,6 +142,9 @@ extension SettingsTableViewController {
assert(field == .exportZipArchive)
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.id, for: indexPath) as! TunnelSettingsTableViewButtonCell
cell.buttonText = field.rawValue
cell.onTapped = { [weak self] in
self?.exportConfigurationsAsZipFile(sourceView: cell.button)
}
return cell
}
}

View File

@ -71,7 +71,7 @@ class TunnelsListTableViewController: UITableViewController {
}
@objc func settingsButtonTapped(sender: UIBarButtonItem!) {
let settingsVC = SettingsTableViewController()
let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager)
let settingsNC = UINavigationController(rootViewController: settingsVC)
settingsNC.modalPresentationStyle = .formSheet
self.present(settingsNC, animated: true)

View File

@ -5,11 +5,29 @@ import Foundation
enum ZipArchiveError: Error {
case cantOpenInputZipFile
case cantOpenOutputZipFileForWriting
case badArchive
}
class ZipArchive {
static func archive(inputs: [(fileName: String, contents: Data)], to destinationURL: URL) throws {
let destinationPath = destinationURL.path
guard let zipFile = zipOpen(destinationPath, APPEND_STATUS_CREATE) else {
throw ZipArchiveError.cantOpenOutputZipFileForWriting
}
for input in inputs {
let fileName = input.fileName
let contents = input.contents
zipOpenNewFileInZip(zipFile, fileName.cString(using: .utf8), nil, nil, 0, nil, 0, nil, Z_DEFLATED, Z_DEFAULT_COMPRESSION)
contents.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> Void in
zipWriteInFileInZip(zipFile, UnsafeRawPointer(ptr), UInt32(contents.count))
}
zipCloseFileInZip(zipFile)
}
zipClose(zipFile, nil)
}
static func unarchive(url: URL, requiredFileExtensions: [String]) throws -> [(fileName: String, contents: Data)] {
var results: [(fileName: String, contents: Data)] = []