aboutsummaryrefslogblamecommitdiffstats
path: root/WireGuard/WireGuard/ZipArchive/ZipArchive.swift
blob: 2cca880a479570cbdba2448ea45852cd33ff4434 (plain) (tree)
1
2
3
4
5
6
7
8
9
                               
                                                             


                 
                                         
                             
                                        
                   
 
                              
                     
                                   
                                                                                                 
                                              
                                                                                                                       
                         
                                                                             

         



                  
















                                                                                                                                     
                                                                                                                          
 
                                                                
                                                                                             






                                                      
                                                                                           


                                       
                                                                                                 
 
                                            







                                                                                          
                                                                                                                                                             
 


                                                                                                                             
 
                                                                                                              



                                                                                           
                                      
                                                                                                           
                                                                         


                                                     
                                     
                                                                                                                         

             
                                                                                                  

                                                           
                                              
 
                                                            





                                            
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.

import Foundation

enum ZipArchiveError: WireGuardAppError {
    case cantOpenInputZipFile
    case cantOpenOutputZipFileForWriting
    case badArchive

    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"))
        }
    }
}

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 -> [(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
        }
    }
}