1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import Foundation
enum ZipArchiveError: WireGuardAppError {
case cantOpenInputZipFile
case cantOpenOutputZipFileForWriting
case badArchive
func alertText() -> (String, String)? {
switch (self) {
case .cantOpenInputZipFile:
return ("Unable to read zip archive", "The zip archive could not be read.")
case .cantOpenOutputZipFileForWriting:
return ("Unable to create zip archive", "Could not open zip file for writing.")
case .badArchive:
return ("Unable to read zip archive", "Bad or corrupt zip archive.")
}
}
}
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)] = []
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
}
if let fileURL = URL(string: String(cString: fileNameBuffer)),
!fileURL.hasDirectoryPath,
requiredFileExtensions.contains(fileURL.pathExtension) {
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<UInt8>) -> Data in
return Data(bytes: buf, count: Int(bytesRead))
}
unzippedData.append(dataRead)
}
} while (bytesRead > 0)
results.append((fileName: fileURL.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
}
}
}
|