aboutsummaryrefslogtreecommitdiffstats
path: root/WireGuard/WireGuard/ZipArchive/ZipArchive.swift
blob: 2faa5164011f679100275355a7a21d6d98b7d81e (plain) (blame)
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 = 1024
            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
        }
    }
}