// SPDX-License-Identifier: MIT // Copyright © 2018 WireGuard LLC. All Rights Reserved. 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) -> 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)] = [] 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 = 1024 var fileNameBuffer = UnsafeMutablePointer.allocate(capacity: bufferSize) var dataBuffer = UnsafeMutablePointer.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 fileName = String(cString: fileNameBuffer) let fileExtension = URL(string: fileName)?.pathExtension ?? "" if (requiredFileExtensions.contains(fileExtension)) { 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) { (buf: UnsafeMutablePointer) -> Data in return Data(bytes: buf, count: Int(bytesRead)) } unzippedData.append(dataRead) } } while (bytesRead > 0) results.append((fileName: fileName, 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 } } }