zip file I/O library for iOS, macOS and tvOS

Related tags

ZipZap
Overview

Build Status

ZipZap is a zip file I/O library for iOS, macOS and tvOS.

The zip file is an ideal container for compound Objective-C documents. Zip files are widely used and well understood. You can randomly access their parts. The format compresses decently and has extensive operating system and tool support. So we want to make this format an even easier choice for you. Thus, the library features:

  • Easy-to-use interface: The public API offers just two classes! Yet you can look through zip files using familiar NSArray collections and properties. And you can zip, unzip and rezip zip files through familiar NSData, NSStream and Image I/O classes.
  • Efficient implementation: We've optimized zip file reading and writing to reduce virtual memory pressure and disk file thrashing. Depending on how your compound document is organized, updating a single entry can be faster than writing the same data to a separate file.
  • File format compatibility: Since ZipZap closely follows the zip file format specification, it works with most Mac, Linux and Windows zip tools.

Install

As an independent project:

  • In the Terminal, run git clone https://github.com/pixelglow/ZipZap.git.
  • Within the ZipZap directory, open the ZipZap.xcodeproj Xcode project.
  • In the Xcode project, select either the ZipZap (iOS Framework), ZipZap (iOS Static Library), ZipZap (macOS Framework), ZipZap (macOS Static Library), ZipZap (tvOS Framework) or ZipZap (tvOS Static Library) scheme from the drop down.
  • You can now build, test (macOS only) or analyze with the selected scheme.
  • The built libraries and test cases are in a subdirectory of ~/Library/Developer/Xcode/DerivedData.

As a project integrated with your own workspace:

  • In the Terminal, run cd workspace then git submodule add https://github.com/pixelglow/ZipZap.git.
  • In your Xcode workspace, choose the File > Add Files to "workspace" menu item, then within the ZipZap directory pick the ZipZap.xcodeproj Xcode project.
  • In any project target that will use ZipZap:
    • In Build Phases > Link Binary With Libraries, add the corresponding libZipZap.a and any other library listed in the Require Link section below.
    • Under Build Settings > Search Paths > Header Search Paths, add ../ZipZap.
  • You can now build, test or analyze those project targets.

Use

Header includes:

#import <ZipZap/ZipZap.h>

Reading an existing zip file:

ZZArchive* oldArchive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath:@"/tmp/old.zip"]
                                            error:nil];
ZZArchiveEntry* firstArchiveEntry = oldArchive.entries[0];
NSLog(@"The first entry's uncompressed size is %lu bytes.", (unsigned long)firstArchiveEntry.uncompressedSize);
NSLog(@"The first entry's data is: %@.", [firstArchiveEntry newDataWithError:nil]);

Writing a new zip file:

ZZArchive* newArchive = [[ZZArchive alloc] initWithURL:[NSURL fileURLWithPath:@"/tmp/new.zip"]
                                               options:@{ZZOpenOptionsCreateIfMissingKey : @YES}
                                                 error:nil];
[newArchive updateEntries:
                     @[
                     [ZZArchiveEntry archiveEntryWithFileName:@"first.text"
                                                     compress:YES
                                                    dataBlock:^(NSError** error)
                                                          {
                                                              return [@"hello, world" dataUsingEncoding:NSUTF8StringEncoding];
                                                          }]
                     ]
                    error:nil];

Updating an existing zip file:

ZZArchive* oldArchive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath:@"/tmp/old.zip"]
                                            error:nil];
[oldArchive updateEntries:
 [oldArchive.entries arrayByAddingObject:
  [ZZArchiveEntry archiveEntryWithFileName:@"second.text"
                                  compress:YES
                                 dataBlock:^(NSError** error)
                                       {
                                           return [@"bye, world" dataUsingEncoding:NSUTF8StringEncoding];
                                       }]]
                    error:nil];

Advanced uses: Recipes

API references: References

Require

  • Build: Xcode 7 and later.
  • Link: Only system libraries; no third-party libraries needed.
    • ApplicationServices.framework (macOS) or ImageIO.framework (iOS, tvOS)
    • Foundation.framework
    • libz.dylib
  • Run: macOS 10.11 (El Capitan), iOS 9.3 or tvOS 9.2 and later.

Support

License

ZipZap is licensed with the BSD license.

Donate

Issues
  • Standard PK (weak) decryption support, with basis for future AES decryption support.

    Standard PK (weak) decryption support, with basis for future AES decryption support.

    I may work on this a little more soon, to add AES. I do not really need encryption right now, only decryption. So I'm not sure If I'll implement.

    opened by danielgindi 40
  • AES decryption support!

    AES decryption support!

    Took me a while, as WinZip's way of using AES was a little different than what I'm used to. Also I changed the AES block processing code a few times... You may want to modify the git history to your liking ;-)

    opened by danielgindi 34
  • XCode profiler throws errors processing ZZHeaders.h

    XCode profiler throws errors processing ZZHeaders.h

    I think this is because it's treating ZZHeaders.h as a C header file, not a C++ header file.

    Screen Shot 2013-02-26 at 9 04 23 AM

    opened by jcollas 16
  • Use of zipzap CocoaPod disables exception support project-wide

    Use of zipzap CocoaPod disables exception support project-wide

    As described in some of the comments on #71, the mere inclusion of the zipzap CocoaPod in a project seems to have the effect of turning off support for Objective-C exceptions on a project-wide basis. For existing code and dependencies that are not easily altered, this is a blocker.

    Earlier, I addressed the problem by ensuring a project-level setting was in force for GCC_ENABLE_OBJC_EXCEPTIONS = YES. After updating to CocoaPods 0.34.1 today and ensuring that a complete list of link_with targets are specified in my Podfile, this no longer works—the setting is superseded project-wide by zipzap.

    As a further work-around, I moved this build setting to target-level on all affected targets in the project. While this now allows a build, it first creates a ton of spew when pod install is run:

    [!] The `Kashoo [Development]` target overrides the `GCC_ENABLE_OBJC_EXCEPTIONS` build setting defined in `Pods/Target Support Files/Pods/Pods.development.xcconfig'. This can lead to problems with the CocoaPods installation
        - Use the `$(inherited)` flag, or
        - Remove the build settings from the target.
    

    This message is repeated for every build configuration on every target (4 targets x 3 configurations = 12 warnings in my case).

    opened by zygoat 16
  • Error handling

    Error handling

    At present Zipzap makes relatively little allowance for error-handling, which worries me:

    • [x] In ZZFileChannel, the call to open might fail due to the URL being unsuitable, or a permissions problem. There is no way to indicate that to the caller beyond the likelihood of NSFileHandle blowing up
    • [x] If writing data to the file handle fails midway through, NSFileHandle is documented to throw an exception. I don't think the code handles this at present, and users of Zipzap aren't advised that such a situation might arise
    • [x] Also in ZZFileChannel, the -openInput method completely ignores the NSError object, so clients have no idea why it might have failed
    opened by mikeabdullah 15
  • In-memory zip generation

    In-memory zip generation

    In my app, I'd like to generate the data for a zip file entirely in-memory without touching the disk. The data is to be uploaded to a remote server, so there's no need for it to touch the local disk, and avoiding so means no file access errors can occur.

    Looking through ZZMutableArchive, this looks a bit tricky to achieve. It seems the current code is built atop NSFileHandle. I could create a custom subclass of NSFileHandle that is backed by data, but that seems rather messy, and perhaps would fail in some places.

    Using NSOutputStream instead would make my life easier. So, what in NSFileHandle are we dependent on? Looking at the code:

    • -offsetInFile to track how much data has been written so far; this could be replaced by passing round integers as arguments/return values
    • If an error occurs while writing an entry, winds the file handle back to where it was before that entry was written
    • -truncateFileAtOffset called once writing is finished; as I understand it, that mostly is to handle archives which are now smaller than when they were read

    Have I got those right, missed anything?

    opened by mikeabdullah 13
  • Not being able to install zipzap on my project

    Not being able to install zipzap on my project

    I've tried to install it with what you wrote on the README but I'm not doing it successfully. That would be nice to post a step-by-step picture 'tutorial'. Thank you

    question 
    opened by pedrovieira 12
  • Instantiation of decrypted stream is crashing for some zip file (AES 256)

    Instantiation of decrypted stream is crashing for some zip file (AES 256)

    Hi there.

    I am experimenting some crash when trying to unarchive a specific AES256 encrypted zip. The following invocation will crash:

    decryptedStream = [[ZZAESDecryptInputStream alloc] initWithStream:dataStream password:password header:_localFileHeader->fileData() strength:_localFileHeader->extraField<ZZWinZipAESExtraField>()->encryptionStrength error:error]; }

    This is because in some cases the _localFileHeader doesn't contain any data and as a result the encryptionStrength can't be extracted. I have found out that the file was corrupted but i believe we should return an error in this case.

    I have now added this additional sanity check before the creation of the localFileHeader: nextCentralFileHeader->relativeOffsetOfLocalHeader == 0 && *beginContent == '\0

    for the following section:

    NSMutableArray<ZZArchiveEntry*>* entries = [NSMutableArray array];
        for (NSUInteger index = 0; index < endOfCentralDirectoryRecord->totalNumberOfEntriesInTheCentralDirectory; ++index)
        {
            // sanity check:
            if (
                // correct signature
                nextCentralFileHeader->sign != ZZCentralFileHeader::sign
                // single disk zip
                || nextCentralFileHeader->diskNumberStart != 0
                // local file occurs before first central file header, and has enough minimal space for at least local file
                || nextCentralFileHeader->relativeOffsetOfLocalHeader + sizeof(ZZLocalFileHeader)
                    > endOfCentralDirectoryRecord->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber
                // next central file header in sequence is within the central directory
                || (const uint8_t*)nextCentralFileHeader->nextCentralFileHeader() > endOfCentralDirectory
                || nextCentralFileHeader->relativeOffsetOfLocalHeader == 0 && *beginContent == '\0')
                return ZZRaiseErrorNo(error, ZZCentralFileHeaderReadErrorCode, @{ZZEntryIndexKey : @(index)});
    
            ZZLocalFileHeader* nextLocalFileHeader = (ZZLocalFileHeader*)(beginContent
                                                                          + nextCentralFileHeader->relativeOffsetOfLocalHeader);
    
            [entries addObject:[[ZZOldArchiveEntry alloc] initWithCentralFileHeader:nextCentralFileHeader
                                                                    localFileHeader:nextLocalFileHeader]];
    
            nextCentralFileHeader = nextCentralFileHeader->nextCentralFileHeader();
        }
    
    
    

    What do you think? If this sounds correct to you i will make a pull request.

    Thanks in advance for your input on that matter.

    opened by ghost 12
  • Podfile excluding headers

    Podfile excluding headers

    Zipzap has been changed to include a list of public headers in the podspec and the only one listed is zipzap.h. Unfortunately this means that the headers that one references aren't exported. Removing the public_headers line fixes the issue, but I'd assume adding the list of all headers an external project should be able to use would fix this too.

    Steps to reproduce:

    • Create a new project in Xcode
    • Add a Podfile with zipzap listed as the only pod
    • Run pod install
    • Add the import for zipzap.h to any class in the project
    • Build
    • Observe error in zipzap.h: "ZZArchive.h file not found"
    opened by parrots 10
  • Document how to work with directories

    Document how to work with directories

    I see there's support for creating a ZZArchiveEntry that represents a directory, but have no idea how to actually populate it, or indeed find out its contents.

    question 
    opened by mikeabdullah 10
  • Swift Package

    Swift Package

    Can anyone tell me how to import the ZipZap dependency into our XCode project without using Swift as there is no Swift package available?

    Showing All Messages https://github.com/pixelglow/ZipZap.git has no Package.swift manifest

    opened by Iain2612 0
  • How to set the firstExtraField?

    How to set the firstExtraField?

    How to set the firstExtraField?

    opened by gara001001 0
  • New clang compiler complains tautology in this code

    New clang compiler complains tautology in this code

    https://github.com/pixelglow/ZipZap/blob/master/ZipZap/ZZArchive.mm#L149

    This line pointing to a static int sign: https://github.com/pixelglow/ZipZap/blob/master/ZipZap/ZZHeaders.h#L151

    Should this be signature?

    opened by liuliu 1
  •  I hope to increase the encryption zip function.

    I hope to increase the encryption zip function.

    I hope to increase the compression encryption zip

    opened by yyx1111 1
  • Archiving release version of ZipZap

    Archiving release version of ZipZap

    Hi,

    When archiving ZipZap an empty archive is created. This can be remedied by setting the 'Build Setting' 'Skip Install' to 'No' for Release builds. Please consider this change.

    Kind regards,

    Remco Poelstra

    opened by rpoelstra 0
  • Unable to Build Xcode 10 Beta

    Unable to Build Xcode 10 Beta

    Xcode has appletvsimulator12.0 installed and id:BD6947F6-C0D6-43C0-B35A-30C3F83A48CE matches

    xcodebuild: error: Unable to find a destination matching the provided destination specifier:
    		{ platform:tvOS Simulator, id:BD6947F6-C0D6-43C0-B35A-30C3F83A48CE }
    
    	Ineligible destinations for the "ZipZap (tvOS Framework)" scheme:
    		{ platform:tvOS, id:dvtdevice-DVTiOSDevicePlaceholder-appletvos:placeholder, name:Generic tvOS Device }
    		{ platform:tvOS Simulator, id:dvtdevice-DVTiOSDeviceSimulatorPlaceholder-appletvsimulator:placeholder, name:Generic tvOS Simulator Device }```
    
    opened by shaynem 0
  • Standardize the filename of Archive Entries so path traversal characters cannot be used to manipulate file output location

    Standardize the filename of Archive Entries so path traversal characters cannot be used to manipulate file output location

    This PR aims to eliminate the Zipperdown security vulnerability. Relevant Github issue

    Files inside of an archive may contain path traversal characters in their filename. When unarchiving these, SSZipArchive follows ../ sequences which can lead to a file being written to an unexpected destination. This could potentially lead to RCE under the worst of circumstances (such as overwriting a javascript file that the app is going to execute).

    This addition sanitizes dot-dot-slash (../) characters from the destination path of files being unarchived, mimicking the default behavior of the Archive Utility found on macOS (as well as most other popular desktop archiving clients).

    Consider an archive containing the following file: ../../../../../../../../../../../Users/test/Desktop/hello.txt

    When unarchived using the current release of ZipZap, the directory traversal characters are followed causing unarchived files to break out of the current directory, resulting in /Users/test/Desktop/hello.txt being created.

    Compare this to the default behavior of macOS's Archive Utility, which does not follow directory traversal characters. The same archive results in extraction restricted to the current directory: %current_path%/Users/test/Desktop/hello.txt

    From a security standpoint, discarding directory traversal characters when unarchiving should be the default behavior of this SDK.

    opened by EthanArbuckle 0
  • Zipperdown security issue (Path traversal symbols not being ignored)

    Zipperdown security issue (Path traversal symbols not being ignored)

    Hello,

    A security issue has been discovered in another popular Archiving SDK, ZipArchive, which can lead to arbitrary file overwrite. The archive can potentially contain path traversal file names, which can lead to files being written outside of their intended destination. This could potentially lead to RCE under the worst of circumstances (such as overwriting a javascript file that the app is going to execute).

    See: https://zipperdown.org/ https://github.com/ZipArchive/ZipArchive/issues/453

    ZipArchive is floating the idea of a "secure" unarchiving method that strips out filenames containing path traversal symbols.

    Your thoughts?

    opened by EthanArbuckle 2
  • newDataWithError is nil

    newDataWithError is nil

    Open ZipArchive is OK, but when I create data from ZZEntry, I just get nil, Please help. PS: The zip file created by ZipZap is OK, however, the other zip files created by other software will be error. Code like this:

        bool UnzipFileAtPath(char* srcFile, char* destPath)
        {
            NSFileManager* fileManager = [NSFileManager defaultManager];
            NSString* zipFilePath = [NSString stringWithUTF8String:srcFile];
            NSString* destDirPath = [NSString stringWithUTF8String:destPath];
            if(![destDirPath hasSuffix:@"/"])
            {
                destDirPath = [NSString stringWithFormat:@"%@/", destDirPath];
            }
            NSLog(@"[ZipZap] Begin Unzip File:%@", zipFilePath);
            
            NSError * error;
            NSData* rawZipData = [fileManager contentsAtPath:zipFilePath];
            NSLog(@"[ZipZap] Raw Zip Data Length: %li", rawZipData.length);
            ZZArchive* zipArchive = [ZZArchive archiveWithData:rawZipData error:&error];
            if (error)
            {
                NSLog(@"[ZipZap] Error happened while open zip file: %@ Error: %@", zipFilePath, [error localizedDescription]);
                return NO;
            }
            else
            {
                NSLog(@"[ZipZap] Zip Entries: %lui", zipArchive.entries.count);
            }
            
            for (ZZArchiveEntry* entry in zipArchive.entries)
            {
                NSString* entryFilePath = [destDirPath stringByAppendingString:entry.fileName];
                if(entry.fileMode & S_IFDIR)
                {
                    [fileManager createDirectoryAtPath:entryFilePath withIntermediateDirectories:YES attributes:nil error:nil];
                }
                else
                {
                    NSError* createDataError;
                    NSData* newData = [entry newDataWithError:&createDataError];
                    if(createDataError || newData == nil || newData.length == 0)
                    {
                        NSLog(@"[ZipZap] Create Data from ZZEntry Error: %lui", createDataError.code);
                        return NO;
                    }
                    else
                    {
                        NSUInteger length = [newData length];
                        NSLog(@"[ZipZap] File: %@, Length: %lu", entry.fileName, length);
                    }
                    NSError* writeDataError;
                    BOOL ret = [fileManager createFileAtPath:entryFilePath contents:newData attributes:nil];
                    if(!ret)
                    {
                        NSLog(@"[ZipZap] Write Data Error: %@, Error: %li", entry.fileName, [writeDataError code]);
                    }
                }
            }
            NSLog(@"[ZipZap] Unzip File OK!");
            return YES;
        }
    
    opened by passingwind 0
  • Support iOS 9

    Support iOS 9

    I see that you start support from iOS 9.3 however I run it on iOS 9.0 and works fine. Is there any drawback If i use it on iOS 9.0 ?

    opened by Kmohamed 2
Releases(8.1)
  • 8.1(Nov 7, 2015)

    • Fix last modified date inaccurately stored.
    • Fix exception build settings, especially with CocoaPods.
    • Target iOS, OS X and tvOS frameworks.
    • Add modern initializers, nullability and lightweight generics.
    • Add fine-grained filename encoding.

    Note: this is a breaking change for accessing non-ASCII zip entry fileName created by non-conforming zip programs e.g. the Mac OS X zip command line. Use fileNameWithEncoding: to force a particular encoding instead.

    Source code(tar.gz)
    Source code(zip)
  • 8.0(Sep 3, 2014)

    • Fix low mmap underflow crash.
    • Fix inflate buffer leak when decompressing entire data.
    • Fix library not linking on simulator build.
    • Ensure fileData is actually copied when reading uncompressed zip files.
    • Decrypting with wrong password now errors immediately
    • Improve archive init API.
    Source code(tar.gz)
    Source code(zip)
  • 5.0(Mar 24, 2014)

  • 6.0(Mar 24, 2014)

    • Fix temporary directories not being removed.
    • Fix output stream status not updating.
    • Fix writing entries >2G erroring with POSIX error EINVAL.
    • Reduce memory usage.
    • newData and newStream API
    Source code(tar.gz)
    Source code(zip)
  • 7.0(Mar 24, 2014)

Owner
Glen Low
Glen Low
Swift framework for zipping and unzipping files.

Zip A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip. Usage Import Zip at the top of the Swift file

Roy Marmelstein 2.1k Sep 17, 2021
ZipArchive is a simple utility class for zipping and unzipping files on iOS, macOS and tvOS.

SSZipArchive ZipArchive is a simple utility class for zipping and unzipping files on iOS, macOS and tvOS. Unzip zip files; Unzip password protected zi

ZipArchive 4.9k Sep 17, 2021
FileManager replacement for Local, iCloud and Remote (WebDAV/FTP/Dropbox/OneDrive) files -- Swift

This Swift library provide a swifty way to deal with local and remote files and directories in a unified way. This library provides implementaion of W

Amir Abbas Mousavian 770 Sep 15, 2021
Simple and expressive file management in Swift

Installation • Usage • License • Documentation FileKit is a Swift framework that allows for simple and expressive file management. Development happens

Nikolai Vazquez 2.2k Sep 17, 2021
A micro-framework for observing file changes, both local and remote. Helpful in building developer tools.

KZFileWatchers Wouldn't it be great if we could adjust feeds and configurations of our native apps without having to sit back to Xcode, change code, r

Krzysztof Zabłocki 1k Sep 14, 2021