aboutsummaryrefslogtreecommitdiffstats
path: root/Sources/WireGuardApp/ZipArchive/ZipArchive.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/WireGuardApp/ZipArchive/ZipArchive.swift')
-rw-r--r--Sources/WireGuardApp/ZipArchive/ZipArchive.swift105
1 files changed, 105 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/ZipArchive/ZipArchive.swift b/Sources/WireGuardApp/ZipArchive/ZipArchive.swift
new file mode 100644
index 0000000..9c2f634
--- /dev/null
+++ b/Sources/WireGuardApp/ZipArchive/ZipArchive.swift
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+enum ZipArchiveError: WireGuardAppError {
+ case cantOpenInputZipFile
+ case cantOpenOutputZipFileForWriting
+ case badArchive
+ case noTunnelsInZipArchive
+
+ var alertText: AlertText {
+ switch self {
+ case .cantOpenInputZipFile:
+ return (tr("alertCantOpenInputZipFileTitle"), tr("alertCantOpenInputZipFileMessage"))
+ case .cantOpenOutputZipFileForWriting:
+ return (tr("alertCantOpenOutputZipFileForWritingTitle"), tr("alertCantOpenOutputZipFileForWritingMessage"))
+ case .badArchive:
+ return (tr("alertBadArchiveTitle"), tr("alertBadArchiveMessage"))
+ case .noTunnelsInZipArchive:
+ return (tr("alertNoTunnelsInImportedZipArchiveTitle"), tr("alertNoTunnelsInImportedZipArchiveMessage"))
+ }
+ }
+}
+
+enum ZipArchive {}
+
+extension 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 { rawBufferPointer -> Void in
+ zipWriteInFileInZip(zipFile, rawBufferPointer.baseAddress, UInt32(contents.count))
+ }
+ zipCloseFileInZip(zipFile)
+ }
+ zipClose(zipFile, nil)
+ }
+
+ static func unarchive(url: URL, requiredFileExtensions: [String]) throws -> [(fileBaseName: String, contents: Data)] {
+
+ var results = [(fileBaseName: String, contents: Data)]()
+ var requiredFileExtensionsLowercased = requiredFileExtensions.map { $0.lowercased() }
+
+ guard let zipFile = unzOpen64(url.path) else {
+ throw ZipArchiveError.cantOpenInputZipFile
+ }
+ defer {
+ unzClose(zipFile)
+ }
+ guard unzGoToFirstFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
+
+ var resultOfGoToNextFile: Int32
+ repeat {
+ guard unzOpenCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
+
+ let bufferSize = 16384 // 16 KiB
+ var fileNameBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
+ var dataBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
+
+ defer {
+ fileNameBuffer.deallocate()
+ dataBuffer.deallocate()
+ }
+
+ guard unzGetCurrentFileInfo64(zipFile, nil, fileNameBuffer, UInt(bufferSize), nil, 0, nil, 0) == UNZ_OK else { throw ZipArchiveError.badArchive }
+
+ let lastChar = String(cString: fileNameBuffer).suffix(1)
+ let isDirectory = (lastChar == "/" || lastChar == "\\")
+ let fileURL = URL(fileURLWithFileSystemRepresentation: fileNameBuffer, isDirectory: isDirectory, relativeTo: nil)
+
+ if !isDirectory && requiredFileExtensionsLowercased.contains(fileURL.pathExtension.lowercased()) {
+ var unzippedData = Data()
+ var bytesRead: Int32 = 0
+ repeat {
+ bytesRead = unzReadCurrentFile(zipFile, dataBuffer, UInt32(bufferSize))
+ if bytesRead > 0 {
+ let dataRead = dataBuffer.withMemoryRebound(to: UInt8.self, capacity: bufferSize) {
+ return Data(bytes: $0, count: Int(bytesRead))
+ }
+ unzippedData.append(dataRead)
+ }
+ } while bytesRead > 0
+ results.append((fileBaseName: fileURL.deletingPathExtension().lastPathComponent, contents: unzippedData))
+ }
+
+ guard unzCloseCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
+
+ resultOfGoToNextFile = unzGoToNextFile(zipFile)
+ } while resultOfGoToNextFile == UNZ_OK
+
+ if resultOfGoToNextFile == UNZ_END_OF_LIST_OF_FILE {
+ return results
+ } else {
+ throw ZipArchiveError.badArchive
+ }
+ }
+}