From 203d6b459610de0111eb57280f92f4d29eee32ab Mon Sep 17 00:00:00 2001 From: Roopesh Chander Date: Wed, 14 Nov 2018 18:52:10 +0530 Subject: [PATCH] Importing: Refactor out zip importing into a separate class Signed-off-by: Roopesh Chander --- WireGuard/WireGuard.xcodeproj/project.pbxproj | 4 ++ .../WireGuard/UI/iOS/ErrorPresenter.swift | 8 +++ .../iOS/TunnelsListTableViewController.swift | 52 ++++--------------- .../WireGuard/ZipArchive/ZipImporter.swift | 48 +++++++++++++++++ 4 files changed, 69 insertions(+), 43 deletions(-) create mode 100644 WireGuard/WireGuard/ZipArchive/ZipImporter.swift diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index 508b0f9..8630b58 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 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 */; }; + 6FE254FB219C10800028284D /* ZipImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE254FA219C10800028284D /* ZipImporter.swift */; }; 6FF4AC1F211EC472002C96EB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC1E211EC472002C96EB /* Assets.xcassets */; }; 6FF4AC22211EC472002C96EB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF4AC20211EC472002C96EB /* LaunchScreen.storyboard */; }; 6FFA5D8921942F320001E2F7 /* PacketTunnelSettingsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C472183C6A3000F85AD /* PacketTunnelSettingsGenerator.swift */; }; @@ -125,6 +126,7 @@ 6FDEF801218646B900D8FBF6 /* ZipArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipArchive.swift; sourceTree = ""; }; 6FDEF805218725D200D8FBF6 /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; }; 6FDEF8072187442100D8FBF6 /* WgQuickConfigFileWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WgQuickConfigFileWriter.swift; sourceTree = ""; }; + 6FE254FA219C10800028284D /* ZipImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipImporter.swift; sourceTree = ""; }; 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 = ""; }; 6FF4AC21211EC472002C96EB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -266,6 +268,7 @@ 6FDEF7E72186320E00D8FBF6 /* ZipArchive */ = { isa = PBXGroup; children = ( + 6FE254FA219C10800028284D /* ZipImporter.swift */, 6FDEF801218646B900D8FBF6 /* ZipArchive.swift */, 6FDEF7F421863B6100D8FBF6 /* 3rdparty */, ); @@ -529,6 +532,7 @@ 6F628C3D217F09E9003482A3 /* TunnelViewModel.swift in Sources */, 6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */, 6FDEF8082187442100D8FBF6 /* WgQuickConfigFileWriter.swift in Sources */, + 6FE254FB219C10800028284D /* ZipImporter.swift in Sources */, 6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */, 6F7774E82172020C006A79B3 /* Configuration.swift in Sources */, 6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */, diff --git a/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift b/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift index fd56588..abc0083 100644 --- a/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift +++ b/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift @@ -24,6 +24,14 @@ class ErrorPresenter { case TunnelActivationError.tunnelActivationFailed: return ("Activation failure", "The tunnel could not be activated due to an internal error") + // Importing a zip file + case ZipArchiveError.cantOpenInputZipFile: + return ("Unable to read zip archive", "The zip archive could not be read.") + case ZipArchiveError.badArchive: + return ("Unable to read zip archive", "Bad or corrupt zip archive.") + case ZipImporterError.noTunnelsInZipArchive: + return ("No tunnels in zip archive", "No .conf tunnel files were found inside the zip archive.") + default: os_log("ErrorPresenter: Error not presented: %{public}@", log: OSLog.default, type: .error, "\(error)") return nil diff --git a/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift b/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift index af06eb6..796c030 100644 --- a/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift +++ b/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift @@ -158,62 +158,28 @@ class TunnelsListTableViewController: UIViewController { } func importFromFile(url: URL) { + guard let tunnelsManager = tunnelsManager else { return } if (url.pathExtension == "zip") { - var unarchivedFiles: [(fileName: String, contents: Data)] = [] + let zipImporter = ZipImporter(url: url) + let configs: [TunnelConfiguration?] do { - unarchivedFiles = try ZipArchive.unarchive(url: url, requiredFileExtensions: ["conf"]) - } catch ZipArchiveError.cantOpenInputZipFile { - showErrorAlert(title: "Unable to read zip archive", message: "The zip archive could not be read.") - return - } catch ZipArchiveError.badArchive { - showErrorAlert(title: "Unable to read zip archive", message: "Bad or corrupt zip archive.") - return + configs = try zipImporter.importConfigFiles() } catch (let error) { - showErrorAlert(title: "Unable to read zip archive", message: "Unexpected error: \(String(describing: error))") + ErrorPresenter.showErrorAlert(error: error, from: self) return } - - for (i, unarchivedFile) in unarchivedFiles.enumerated().reversed() { - let fileBaseName = URL(string: unarchivedFile.fileName)?.deletingPathExtension().lastPathComponent - if let trimmedName = fileBaseName?.trimmingCharacters(in: .whitespacesAndNewlines), !trimmedName.isEmpty { - unarchivedFiles[i].fileName = trimmedName - } else { - unarchivedFiles.remove(at: i) - } - } - if (unarchivedFiles.isEmpty) { - showErrorAlert(title: "No tunnels in zip archive", message: "No .conf tunnel files were found inside the zip archive.") - return - } - guard let tunnelsManager = tunnelsManager else { return } - unarchivedFiles.sort { $0.fileName < $1.fileName } - var lastFileName: String? - var configs: [TunnelConfiguration] = [] - for file in unarchivedFiles { - if file.fileName == lastFileName { - continue - } - lastFileName = file.fileName - guard let fileContents = String(data: file.contents, encoding: .utf8) else { - continue - } - guard let tunnelConfig = try? WgQuickConfigFileParser.parse(fileContents, name: file.fileName) else { - continue - } - configs.append(tunnelConfig) - } - tunnelsManager.addMultiple(tunnelConfigurations: configs) { [weak self] (numberSuccessful) in - if numberSuccessful == unarchivedFiles.count { + tunnelsManager.addMultiple(tunnelConfigurations: configs.compactMap { $0 }) { [weak self] (numberSuccessful) in + if numberSuccessful == configs.count { return } self?.showErrorAlert(title: "Created \(numberSuccessful) tunnels", - message: "Created \(numberSuccessful) of \(unarchivedFiles.count) tunnels from zip archive") + message: "Created \(numberSuccessful) of \(configs.count) tunnels from zip archive") } } else /* if (url.pathExtension == "conf") -- we assume everything else is a conf */ { let fileBaseName = url.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines) if let fileContents = try? String(contentsOf: url), let tunnelConfiguration = try? WgQuickConfigFileParser.parse(fileContents, name: fileBaseName) { - tunnelsManager?.add(tunnelConfiguration: tunnelConfiguration) { (_, error) in + tunnelsManager.add(tunnelConfiguration: tunnelConfiguration) { (_, error) in if let error = error { ErrorPresenter.showErrorAlert(error: error, from: self) } diff --git a/WireGuard/WireGuard/ZipArchive/ZipImporter.swift b/WireGuard/WireGuard/ZipArchive/ZipImporter.swift new file mode 100644 index 0000000..7ee5c32 --- /dev/null +++ b/WireGuard/WireGuard/ZipArchive/ZipImporter.swift @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018 WireGuard LLC. All Rights Reserved. + +import UIKit + +enum ZipImporterError: Error { + case noTunnelsInZipArchive +} + +class ZipImporter { + let url: URL + init(url: URL) { + self.url = url + } + + func importConfigFiles() throws -> [TunnelConfiguration?] { + var unarchivedFiles: [(fileName: String, contents: Data)] = try ZipArchive.unarchive(url: url, requiredFileExtensions: ["conf"]) + + for (i, unarchivedFile) in unarchivedFiles.enumerated().reversed() { + let fileBaseName = URL(string: unarchivedFile.fileName)?.deletingPathExtension().lastPathComponent + if let trimmedName = fileBaseName?.trimmingCharacters(in: .whitespacesAndNewlines), !trimmedName.isEmpty { + unarchivedFiles[i].fileName = trimmedName + } else { + unarchivedFiles.remove(at: i) + } + } + + if (unarchivedFiles.isEmpty) { + throw ZipImporterError.noTunnelsInZipArchive + } + + unarchivedFiles.sort { $0.fileName < $1.fileName } + var configs = Array(repeating: nil, count: unarchivedFiles.count) + for (i, file) in unarchivedFiles.enumerated() { + if (i > 0 && file == unarchivedFiles[i - 1]) { + continue + } + guard let fileContents = String(data: file.contents, encoding: .utf8) else { + continue + } + guard let tunnelConfig = try? WgQuickConfigFileParser.parse(fileContents, name: file.fileName) else { + continue + } + configs[i] = tunnelConfig + } + return configs + } +}