diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6d0b34d --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing to UnzipKit + +First of all, if you're reading this, thanks! I love getting feedback from other developers who use this library and welcome any and all feedback. Issues and Pull requests are welcome, with a few guidelines, laid out below. + +# Issues + +I need the following, at a minimum: + +1. The steps to reproduce your issue, detailed enough for me to follow along and see what you're seeing (bonus points for giving me a sample archive that demonstrates the issue) +2. What you expect to happen +3. What actually happened + +If what you're reporting is a crash, a crash report is key (or a stack trace, if you don't have a full crash report). + +Beyond that, the **quickest way** to get me to address what you're asking for is to provide a unit test (or tests) to demonstrate what you'd like to see (they should probably fail). I have a pretty complete set of tests already in the library that you can use as an example if you'd like. + +# Pull Requests + +Pull Requests are always greatly appreciated. The general rule of thumb for how quickly something will make it into a future release is the inverse of how much work I'll need to do on it. Creating a PR instead of an issue report takes a huge burden off of me. If you do create a PR, I'll require these things before I merge it: + +1. Style needs to mesh with the rest of the repo. I'd love to have some style enforcement checks at some point, but don't yet. Do your best according to what you see in the rest of the source and I'll point out anything you missed. No big deal +2. If you're fixing a bug, I need to see a unit test that reproduces the issue(s) by failing if I comment out your fix. I try to maintain code coverage that's as complete as possible, so more unit tests are always welcome +3. Don't touch the `Lib/MiniZip` directory. This is the MiniZip source code, downloaded from [the zlib project](https://github.com/madler/zlib/tree/master/contrib/minizip), and updated occasionally. You can usually look at the revision history of that folder to see what version it's currently on. This library does cause some compiler warnings, but I ignore them in Xcode and CocoaPods, relying on unit tests to tell me if anything is truly broken, since I don't maintain patches - I expect to be able to drop in newer versions as needed +4. Be patient with the process - I maintain a high attention to detail, and will take however much time is necessary to get the change to where I'd like it. If you'd rather have a more brief back-and-forth, I can always pull into a separate branch to make the changes myself before merging, but enjoy the interaction of collaborating on PRs whenever possible + diff --git a/.github/ISSUE_TEMPLATE.MD b/.github/ISSUE_TEMPLATE.MD index 3453922..d1d3b61 100644 --- a/.github/ISSUE_TEMPLATE.MD +++ b/.github/ISSUE_TEMPLATE.MD @@ -9,7 +9,7 @@ _Delete from this line up_ ## Option A -### Steps to reproduce issue (Detailed enough for me to reproduce): +### Steps to reproduce issue (Detailed enough for me to reproduce - attaching a sample archive can be very helpful): ### What you expect to happen: diff --git a/.gitignore b/.gitignore index 3ee5ebb..04c30e7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ build **/xcuserdata/* analyzer-output/** +# Generated by a Run Script build phase +Source/GeneratedSwiftImport.h + UnzipKitDemo/Pods/* UnzipKitDemo/Podfile.lock UnzipKitDemo/Carthage/* diff --git a/.travis.yml b/.travis.yml index 3434eee..303f93a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,45 @@ language: - objective-c -osx_image: xcode9.3 +osx_image: xcode12.2 + +before_install: + - gem install xcpretty before_script: - # Make log level less verbose. Temporarily undo if more info is needed - - sudo log config --mode "level:default" + # Essentially turn off logging. Temporarily undo if more info is needed + - sudo log config --mode "level:default" --subsystem com.abbey-code.UnzipKit matrix: include: + - stage: Test + env: Name=Mac-Fail-Fast + # The CLANG arguments and find command fail the build on analyzer errors + script: xcodebuild -workspace UnzipKit.xcworkspace -scheme UnzipKit -testPlan Fast -sdk macosx -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output | xcpretty && exit ${PIPESTATUS[0]} && [[ -z `find analyzer-output -name "*.html"` ]] + - stage: Test env: Name=Mac # The CLANG arguments and find command fail the build on analyzer errors - script: xcodebuild -workspace UnzipKit.xcworkspace -scheme UnzipKit -sdk macosx -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output && [[ -z `find analyzer-output -name "*.html"` ]] + script: xcodebuild -workspace UnzipKit.xcworkspace -scheme UnzipKit -testPlan Rigorous -sdk macosx -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output | xcpretty && exit ${PIPESTATUS[0]} && [[ -z `find analyzer-output -name "*.html"` ]] - stage: Test env: Name=iOS # The CLANG arguments and find command fail the build on analyzer errors - script: xcodebuild -workspace UnzipKit.xcworkspace -scheme UnzipKit -destination 'platform=iOS Simulator,name=iPhone 7,OS=latest' -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output && [[ -z `find analyzer-output -name "*.html"` ]] + script: xcodebuild -workspace UnzipKit.xcworkspace -scheme UnzipKit -testPlan Rigorous -destination 'platform=iOS Simulator,name=iPhone 8,OS=latest' -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output | xcpretty && exit ${PIPESTATUS[0]} && [[ -z `find analyzer-output -name "*.html"` ]] - stage: Test env: Name=DemoAppBuild # The CLANG arguments and find command fail the build on analyzer errors - script: ./Scripts/install-demo-libs.sh && xcodebuild -workspace UnzipKitDemo/UnzipKitDemo.xcworkspace -scheme UnzipKitDemo -destination 'platform=iOS Simulator,name=iPhone 7,OS=latest' -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output && [[ -z `find analyzer-output -name "*.html"` ]] + script: ./Scripts/install-demo-libs.sh && xcodebuild -workspace UnzipKitDemo/UnzipKitDemo.xcworkspace -scheme UnzipKitDemo -destination 'platform=iOS Simulator,name=iPhone 8,OS=latest' -configuration Release analyze test ENABLE_NS_ASSERTIONS=1 CLANG_ANALYZER_OUTPUT=html CLANG_ANALYZER_OUTPUT_DIR=analyzer-output | xcpretty && exit ${PIPESTATUS[0]} && [[ -z `find analyzer-output -name "*.html"` ]] - stage: Validate - env: Name=CocoaPods + env: Name=CocoaPods-Framework script: ./Scripts/cocoapod-validate.sh + - stage: Validate + env: Name=CocoaPods-Static-Lib + script: ./Scripts/cocoapod-validate.sh --use-libraries + - stage: Validate env: Name=Carthage script: ./Scripts/carthage-validate.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 371e61c..2fd715b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # UnzipKit CHANGELOG +## 2.0 + +* Removed methods deprecated in v1.9 (Issue #90, PR #92) +* Fixed behavior of `-extractFilesTo:overwrite:error:`, so it shows the progress of each individual file as they extract (Issue #91, PR #94) +* Deprecated the initializers that take a file path instead of an `NSURL` (Issue #90, PR #95) +* Fixed a crasher for unreadable files in `+pathIsAZip:` (Issue #99) +* Deprecated all overloads of `-writeData:...` and `-writeIntoBuffer:...` that take any file properties other than the path, replacing them each with a single call that takes an instance of the new `ZipFileProperties`. This allows for all the default values to be defined in one place, so you can specify only where you want to deviate from the defaults (Issue #89, PR #97) +* Fixed buffer overrun vulnerability when deleting a file in an archive where not every file has a file comment (Issue #106) +* Fixed deallocated pointer use when a file write occurs inside the block of a file write operation, already an error condition (Issue #107) +* Added a test plan so unit tests can run with various sanitizers and checkers in CI and locally +* Improved Xcode 12 compatibility + ## 1.9 * Added support for `NSProgress` and `NSProgressReporting` in all extraction and iteration methods (Issue #32) diff --git a/README.md b/README.md index 89de8d5..d733c73 100644 --- a/README.md +++ b/README.md @@ -97,12 +97,36 @@ You can also modify Zip archives: filePath:@"dir/filename.jpg" error:&error]; ``` +* Write an in-memory `NSData` into the archive with custom properties + + **Objective-C** + + ```Objective-C + ZipFileProperties *props = [[ZipFileProperties alloc] init:someFile]; + props.password = @"secret"; + + BOOL success = [archive writeData:someFile + filePath:@"dir/filename.jpg" + error:&error]; + ``` + + **Swift** + + ```Swift + do { + let props = ZipFileProperties(filePath) + props.password = "secret" + try archive.write(someFile, props: props) + } catch let error as NSError { + NSLog("Error writing to file \(filePath): \(error)") + } + ``` * Write data as a stream to the archive (from disk or over the network), using a block: ```Objective-C - BOOL success = [archive writeIntoBuffer:@"dir/filename.png" - error:&error - block: + BOOL success = [archive writeIntoBufferAtPath:@"dir/filename.png" + error:&error + block: ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { for (NSUInteger i = 0; i <= someFile.length; i += bufferSize) { const void *bytes = // some data @@ -139,9 +163,7 @@ The following methods support `NSProgress` and `NSProgressReporting`: * `performOnDataInArchive:error:` * `extractBufferedDataFromFile:error:action:` * `writeData:filePath:error:`* -* `writeData:filePath:fileDate:error:`* -* `writeData:filePath:fileDate:compressionMethod:password:error:`* -* `writeData:filePath:fileDate:compressionMethod:password:overwrite:error:`* +* `writeData:props:error:`* _* the `writeData...` methods don't support cancellation like the read-only methods do @@ -203,7 +225,7 @@ If you don't have a hierarchy of `NSProgress` instances, or if you want to obser Using either method above, you can call `[progress cancel]` to stop the operation in progress. It will cause the operation to fail, returning `nil` or `NO` (depending on the return type, and give an error with error code `UZKErrorCodeUserCancelled`. -Note: Cancellation is only supported on extraction methods, not write methods. +_Note: Cancellation is only supported on extraction methods, not write methods.__ # Documentation @@ -297,6 +319,28 @@ Before pushing a build, you must: Once that's done, you can call `git push --follow-tags` [1](#f1), and let [Travis CI](https://travis-ci.org/abbeycode/UnzipKit/builds) take care of the rest. +## Re-registering with CocoaPods + +If you see this message in the Release stage of the build, you need to get a new authentication token from CocaPods: + +> Authentication token is invalid or unverified. Either verify it with the email that was sent or register a new session + +1. Locally, call `pod trunk register _pod owner email address_ --description='_describe machine_'` +1. Click the verification link in the email that CocoaPods sends +1. Open ~/.netrc, and look for the section like this: + machine trunk.cocoapods.org + login user@example.com + password 0000000011111111 +1. Update the token in Travis CI: + 1. Go to the [plan settings](https://travis-ci.org/github/abbeycode/UnzipKit/settings) + 1. Delete `COCOAPODS_TRUNK_TOKEN` + 1. Create a new Environment Variable: + * Name: *COCOAPODS_TRUNK_TOKEN* + * Value: *0000000011111111* (substituting the value from you local machine found above) + * Branch: *All Branches* + * Display Value in Build Log: *Leave unchecked* +1. Re-run the Release stage + # License * UnzipKit: [See LICENSE (BSD)](LICENSE) diff --git a/Resources/UnzipKitResources-Info.plist b/Resources/UnzipKitResources-Info.plist index c04e70c..1ca6dc9 100644 --- a/Resources/UnzipKitResources-Info.plist +++ b/Resources/UnzipKitResources-Info.plist @@ -13,11 +13,11 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.9 + 2.0-beta2 CFBundleSignature ???? CFBundleVersion - 1.9 + 2.0-beta2 NSHumanReadableCopyright Copyright © 2019 Abbey Code. All rights reserved. NSPrincipalClass diff --git a/Scripts/add-github-release.py b/Scripts/add-github-release.py index 0e0142f..617190c 100755 --- a/Scripts/add-github-release.py +++ b/Scripts/add-github-release.py @@ -26,7 +26,10 @@ def add_release(token, repo, tag, archive_path, notes): is_beta = tag_is_beta(tag) - url = 'https://api.github.com/repos/{}/releases?access_token={}'.format(repo, token) + url = 'https://api.github.com/repos/{}/releases'.format(repo) + header = { + 'Authorization': 'token {}'.format(token) + } values = { 'tag_name': tag, 'name': 'v{}'.format(tag), @@ -35,7 +38,7 @@ def add_release(token, repo, tag, archive_path, notes): } data = json.dumps(values) - request = urllib2.Request(url, data) + request = urllib2.Request(url, data, header) response = urllib2.urlopen(request) the_page = response.read() @@ -80,8 +83,11 @@ def upload_carthage_archive(token, upload_url, archive_path): ''' upload_url = upload_url.split('{')[0] - url = '{}?access_token={}&name={}'.format(upload_url, token, archive_path) - header = {'Content-Type': 'application/zip'} + url = '{}?name={}'.format(upload_url, archive_path) + header = { + 'Authorization': 'token {}'.format(token), + 'Content-Type': 'application/zip' + } with FileWithLen(archive_path, 'r') as f: request = urllib2.Request(url, f, header) diff --git a/Scripts/cocoapod-validate.sh b/Scripts/cocoapod-validate.sh index 57fcc73..03b1a73 100755 --- a/Scripts/cocoapod-validate.sh +++ b/Scripts/cocoapod-validate.sh @@ -11,6 +11,6 @@ pod env # Using sed to remove logging from output until CocoaPods issue #7577 is implemented and I can use the # OS_ACTIVITY_MODE = disable environment variable from the test spec scheme -pod lib lint --verbose | sed -l '/xctest\[/d; /^$/d' +pod lib lint --verbose "$@" | sed -l '/xctest\[/d; /^$/d' . Scripts/unset-travis-tag.sh \ No newline at end of file diff --git a/Scripts/generate-swift-import-header.sh b/Scripts/generate-swift-import-header.sh new file mode 100755 index 0000000..f97ad06 --- /dev/null +++ b/Scripts/generate-swift-import-header.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# Different styles of import statement need to be used for the Swift generated header, +# depending on the target type (static library or dynamic framework). This script reads +# the PACKAGE_TYPE environment variable Xcode sets to create the correct one at build +# time, allowing the library to be built as either type from CocoaPods + +# static library: com.apple.product-type.library.static +# dynamic framework: com.apple.package-type.wrapper.framework +[[ "${PACKAGE_TYPE}" = "com.apple.package-type.wrapper.framework" ]] \ + && SWIFTIMPORT="<${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}-Swift.h>" \ + || SWIFTIMPORT="\"${PRODUCT_MODULE_NAME}-Swift.h\"" + +if [ -z "$PODS_TARGET_SRCROOT" ]; then + PODS_TARGET_SRCROOT=${SOURCE_ROOT} + echo "Building in Xcode instead of CocoaPods. Overriding PODS_TARGET_SRCROOT with SOURCE_ROOT" +fi + +IMPORT_TEXT=\ +"/** +* +* This header was generated by a Run Script build phase. See +* Scripts/generate-swift-import-header.sh for more information +* +*/ + +#ifndef GeneratedSwiftImport_h +#define GeneratedSwiftImport_h + +#import ${SWIFTIMPORT} + +#endif /* GeneratedSwiftImport_h */ +" +echo "$IMPORT_TEXT" > ${PODS_TARGET_SRCROOT}/Source/GeneratedSwiftImport.h diff --git a/Source/Info.plist b/Source/Info.plist index 1e28140..861f1d9 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.9 + 2.0-beta2 CFBundleSignature ???? CFBundleVersion - 1.9 + 2.0-beta2 NSHumanReadableCopyright © 2017 Abbey Code. All rights reserved. NSPrincipalClass diff --git a/Source/UZKArchive.h b/Source/UZKArchive.h index ce99538..8b6f711 100644 --- a/Source/UZKArchive.h +++ b/Source/UZKArchive.h @@ -7,7 +7,9 @@ #import #import -#import "UZKFileInfo.h" +#import + +@class ZipFileProperties; /** * Defines the various error codes that the listing and extraction methods return. @@ -182,50 +184,11 @@ extern NSString *UZKErrorDomain; * DEPRECATED: Creates and returns an archive at the given path * * @param filePath A path to the archive file - * - * @return Returns a UZKArchive object, or nil if the path isn't reachable - */ -+ (nullable instancetype)zipArchiveAtPath:(NSString *)filePath __deprecated_msg("Use -initWithPath:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given URL - * - * @param fileURL The URL of the archive file - * - * @return Returns a UZKArchive object, or nil if the URL isn't reachable - */ -+ (nullable instancetype)zipArchiveAtURL:(NSURL *)fileURL __deprecated_msg("Use -initWithURL:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given path, with a given password - * - * @param filePath A path to the archive file - * @param password The password of the given archive - * - * @return Returns a UZKArchive object, or nil if the path isn't reachable - */ -+ (nullable instancetype)zipArchiveAtPath:(NSString *)filePath password:(nullable NSString *)password __deprecated_msg("Use -initWithPath:password:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given URL, with a given password - * - * @param fileURL The URL of the archive file - * @param password The password of the given archive - * - * @return Returns a UZKArchive object, or nil if the URL isn't reachable - */ -+ (nullable instancetype)zipArchiveAtURL:(NSURL *)fileURL password:(nullable NSString *)password __deprecated_msg("Use -initWithURL:password:error: instead");; - - -/** - * Creates and returns an archive at the given path - * - * @param filePath A path to the archive file * @param error Returns an error code if the object can't be initialized * * @return Returns a UZKArchive object, or nil if the path isn't reachable */ -- (nullable instancetype)initWithPath:(NSString *)filePath error:(NSError **)error; +- (nullable instancetype)initWithPath:(NSString *)filePath error:(NSError **)error __deprecated_msg("Use -initWithURL:error: instead"); /** * Creates and returns an archive at the given URL @@ -238,7 +201,7 @@ extern NSString *UZKErrorDomain; - (nullable instancetype)initWithURL:(NSURL *)fileURL error:(NSError **)error; /** - * Creates and returns an archive at the given path, with a given password + * DEPRECATED: Creates and returns an archive at the given path, with a given password * * @param filePath A path to the archive file * @param password The password of the given archive @@ -246,7 +209,7 @@ extern NSString *UZKErrorDomain; * * @return Returns a UZKArchive object, or nil if the path isn't reachable */ -- (nullable instancetype)initWithPath:(NSString *)filePath password:(nullable NSString *)password error:(NSError **)error; +- (nullable instancetype)initWithPath:(NSString *)filePath password:(nullable NSString *)password error:(NSError **)error __deprecated_msg("Use -initWithURL:password:error: instead"); /** * Creates and returns an archive at the given URL, with a given password @@ -318,25 +281,6 @@ extern NSString *UZKErrorDomain; overwrite:(BOOL)overwrite error:(NSError **)error; -/** - * **DEPRECATED:** Writes all files in the archive to the given path - * - * @param destinationDirectory The destination path of the unarchived files - * @param overwrite YES to overwrite files in the destination directory, NO otherwise - * @param progress Called every so often to report the progress of the extraction - * - * - *currentFile* The info about the file that's being extracted - * - *percentArchiveDecompressed* The percentage of the archive that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return YES on successful extraction, NO if an error was encountered - */ -- (BOOL)extractFilesTo:(NSString *)destinationDirectory - overwrite:(BOOL)overwrite - progress:(nullable void (^)(UZKFileInfo *currentFile, CGFloat percentArchiveDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractFilesTo:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also * allows cancellation in the middle of extraction @@ -349,22 +293,6 @@ extern NSString *UZKErrorDomain; - (nullable NSData *)extractData:(UZKFileInfo *)fileInfo error:(NSError **)error; -/** - * **DEPRECATED:** Unarchive a single file from the archive into memory - * - * @param fileInfo The info of the file within the archive to be expanded. Only the filename property is used - * @param progress Called every so often to report the progress of the extraction - * - * - *percentDecompressed* The percentage of the archive that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return An NSData object containing the bytes of the file, or nil if an error was encountered - */ -- (nullable NSData *)extractData:(UZKFileInfo *)fileInfo - progress:(nullable void (^)(CGFloat percentDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractData:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also * allows cancellation in the middle of extraction @@ -377,22 +305,6 @@ extern NSString *UZKErrorDomain; - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:(NSError **)error; -/** - * **DEPRECATED:** Unarchive a single file from the archive into memory - * - * @param filePath The path of the file within the archive to be expanded - * @param progress Called every so often to report the progress of the extraction - * - * - *percentDecompressed* The percentage of the file that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return An NSData object containing the bytes of the file, or nil if an error was encountered - */ -- (nullable NSData *)extractDataFromFile:(NSString *)filePath - progress:(nullable void (^)(CGFloat percentDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractDataFromFile:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Loops through each file in the archive into memory, allowing you to perform an action * using its info. Supports NSProgress for progress reporting, which also @@ -496,67 +408,26 @@ extern NSString *UZKErrorDomain; error:(NSError **)error; /** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the - * archive - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - -/** - * Writes the data to the zip file, overwriting it if a file of that name already exists in the archive + * Writes the data to the zip file, overwriting it if a file of that name already exists + * in the archive. Supports NSProgress for progress reporting, which DOES NOT allow cancellation + * in the middle of writing * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param error Contains an NSError object when there was an error writing to the archive + * @param data Data to write into the archive + * @param props Specifies the properties of the file being archived + * @param error Contains an NSError object when there was an error writing to the archive * * @return YES if successful, NO on error */ - (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate + props:(ZipFileProperties *)props error:(NSError **)error; /** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the archive - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - -/** - * Writes the data to the zip file, overwriting it if a file of that name already exists in the archive + * DEPRECATED: Writes the data to the zip file, overwriting it if a file of that name already exists in the archive * * @param data Data to write into the archive * @param filePath The full path to the target file in the archive * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) * @param error Contains an NSError object when there was an error writing to the archive * * @return YES if successful, NO on error @@ -564,23 +435,18 @@ extern NSString *UZKErrorDomain; - (BOOL)writeData:(NSData *)data filePath:(NSString *)filePath fileDate:(nullable NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - error:(NSError **)error; + error:(NSError **)error +__deprecated_msg("Use -writeData:props:error: instead"); /** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the archive + * DEPRECATED: Writes the data to the zip file, overwriting it if a file of that name already exists in the archive * * @param data Data to write into the archive * @param filePath The full path to the target file in the archive * @param fileDate The timestamp of the file in the archive. Uses the current time if nil * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) * @param password Override the password associated with the archive (not recommended) - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive + * @param error Contains an NSError object when there was an error writing to the archive * * @return YES if successful, NO on error */ @@ -589,11 +455,11 @@ compressionMethod:(UZKCompressionMethod)method fileDate:(nullable NSDate *)fileDate compressionMethod:(UZKCompressionMethod)method password:(nullable NSString *)password - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:compressionMethod:password:error: instead, and if using the progress block, replace with NSProgress as described in the README"); + error:(NSError **)error +__deprecated_msg("Use -writeData:props:error: instead"); /** - * Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting + * DEPRECATED: Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, * the size of the archive will grow each time the file is written. @@ -616,10 +482,11 @@ compressionMethod:(UZKCompressionMethod)method compressionMethod:(UZKCompressionMethod)method password:(nullable NSString *)password overwrite:(BOOL)overwrite - error:(NSError **)error; + error:(NSError **)error +__deprecated_msg("Use -writeData:props:error: instead"); /** - * Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting + * DEPRECATED: Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, * the size of the archive will grow each time the file is written. @@ -644,82 +511,41 @@ compressionMethod:(UZKCompressionMethod)method compressionMethod:(UZKCompressionMethod)method password:(nullable NSString *)password overwrite:(BOOL)overwrite - error:(NSError **)error; + error:(NSError **)error +__deprecated_msg("Use -writeData:props:error: instead"); /** - * **DEPRECATED:** Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting - * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before - * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, - * the size of the archive will grow each time the file is written. - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) - * @param overwrite If YES, and the file exists, delete it before writing. If NO, append - * the data into the archive without removing it first (legacy Objective-Zip - * behavior) - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:compressionMethod:password:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting - * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before - * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, - * the size of the archive will grow each time the file is written. - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param permissions The desired POSIX permissions of the file in the archive - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) - * @param overwrite If YES, and the file exists, delete it before writing. If NO, append - * the data into the archive without removing it first (legacy Objective-Zip - * behavior) - * @param progress Called every so often to report the progress of the compression + * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * don't need to reside in memory at once. It overwrites an existing file with the same name. * - * - *percentCompressed* The percentage of the file that has been compressed + * @param filePath The full path to the target file in the archive + * @param error Contains an NSError object when there was an error writing to the archive + * @param action Contains your code to loop through the source bytes and write them to the + * archive. Each time a chunk of data is ready to be written, call writeData, + * passing in a pointer to the bytes and their length. Return YES if successful, + * or NO on error (in which case, you should assign the actionError parameter * - * @param error Contains an NSError object when there was an error writing to the archive + * - *writeData* Call this block to write some bytes into the archive. It returns NO if the + * write failed. If this happens, you should return from the action block, and + * handle the NSError returned into the error reference + * - *actionError* Assign to an NSError instance before returning NO * * @return YES if successful, NO on error */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - posixPermissions:(short)permissions -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:permissions:compressionMethod:password:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); +- (BOOL)writeIntoBufferAtPath:(NSString *)filePath + error:(NSError **)error + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; /** * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name. * - * @param filePath The full path to the target file in the archive - * @param error Contains an NSError object when there was an error writing to the archive - * @param action Contains your code to loop through the source bytes and write them to the - * archive. Each time a chunk of data is ready to be written, call writeData, - * passing in a pointer to the bytes and their length. Return YES if successful, - * or NO on error (in which case, you should assign the actionError parameter + * @param props Specifies the properties of the file being archived + * @param error Contains an NSError object when there was an error writing to the archive + * @param action Contains your code to loop through the source bytes and write them to the + * archive. Each time a chunk of data is ready to be written, call writeData, + * passing in a pointer to the bytes and their length. Return YES if successful, + * or NO on error (in which case, you should assign the actionError parameter * * - *writeData* Call this block to write some bytes into the archive. It returns NO if the * write failed. If this happens, you should return from the action block, and @@ -728,12 +554,12 @@ compressionMethod:(UZKCompressionMethod)method * * @return YES if successful, NO on error */ -- (BOOL)writeIntoBuffer:(NSString *)filePath +- (BOOL)writeIntoBuffer:(ZipFileProperties *)props error:(NSError **)error block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name. * * @param filePath The full path to the target file in the archive @@ -754,10 +580,11 @@ compressionMethod:(UZKCompressionMethod)method - (BOOL)writeIntoBuffer:(NSString *)filePath fileDate:(nullable NSDate *)fileDate error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name. * * @param filePath The full path to the target file in the archive @@ -780,10 +607,11 @@ compressionMethod:(UZKCompressionMethod)method fileDate:(nullable NSDate *)fileDate compressionMethod:(UZKCompressionMethod)method error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name, only if * specified with the overwrite flag. Overwriting presents a tradeoff: the whole archive needs to be * copied (minus the file to be overwritten) before the write begins. For a large archive, this can @@ -814,10 +642,11 @@ compressionMethod:(UZKCompressionMethod)method compressionMethod:(UZKCompressionMethod)method overwrite:(BOOL)overwrite error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name, only if * specified with the overwrite flag. Overwriting presents a tradeoff: the whole archive needs to be * copied (minus the file to be overwritten) before the write begins. For a large archive, this can @@ -851,10 +680,11 @@ compressionMethod:(UZKCompressionMethod)method overwrite:(BOOL)overwrite CRC:(unsigned long)preCRC error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name, only if * specified with the overwrite flag. Overwriting presents a tradeoff: the whole archive needs to be * copied (minus the file to be overwritten) before the write begins. For a large archive, this can @@ -890,11 +720,12 @@ compressionMethod:(UZKCompressionMethod)method CRC:(unsigned long)preCRC password:(nullable NSString *)password error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** - * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents + * DEPRECATED: Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name, only if * specified with the overwrite flag. Overwriting presents a tradeoff: the whole archive needs to be * copied (minus the file to be overwritten) before the write begins. For a large archive, this can @@ -932,7 +763,8 @@ compressionMethod:(UZKCompressionMethod)method CRC:(unsigned long)preCRC password:(nullable NSString *)password error:(NSError **)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +__deprecated_msg("Use -writeIntoBuffer:props:error:block: instead"); /** * Removes the given file from the archive diff --git a/Source/UZKArchive.m b/Source/UZKArchive.m index a8ac32a..d367bdb 100644 --- a/Source/UZKArchive.m +++ b/Source/UZKArchive.m @@ -6,9 +6,10 @@ #import "UZKArchive.h" +#import "GeneratedSwiftImport.h" + #import "zip.h" -#import "UZKFileInfo.h" #import "UZKFileInfo_Private.h" #import "UnzipKitMacros.h" #import "NSURL+UnzipKitExtensions.h" @@ -69,31 +70,6 @@ @implementation UZKArchive @synthesize comment = _comment; -#pragma mark - Deprecated Convenience Methods - - -+ (UZKArchive *)zipArchiveAtPath:(NSString *)filePath -{ - return [[UZKArchive alloc] initWithPath:filePath error:nil]; -} - -+ (UZKArchive *)zipArchiveAtURL:(NSURL *)fileURL -{ - return [[UZKArchive alloc] initWithURL:fileURL error:nil]; -} - -+ (UZKArchive *)zipArchiveAtPath:(NSString *)filePath password:(NSString *)password -{ - return [[UZKArchive alloc] initWithPath:filePath password:password error:nil]; -} - -+ (UZKArchive *)zipArchiveAtURL:(NSURL *)fileURL password:(NSString *)password -{ - return [[UZKArchive alloc] initWithURL:fileURL password:password error:nil]; -} - - - #pragma mark - Initializers + (void)initialize { @@ -148,7 +124,7 @@ - (instancetype)initWithFile:(NSURL *)fileURL password:(NSString*)password error UZKCreateActivity("Init Archive"); if (!fileURL) { - UZKLogError("Nil fileURL passed to UZKArchive initializer") + UZKLogError("Nil fileURL passed to UZKArchive initializer"); return nil; } @@ -166,7 +142,7 @@ - (instancetype)initWithFile:(NSURL *)fileURL password:(NSString*)password error return nil; } } else { - UZKLogInfo("URL %{public}@ doesn't yet exist", fileURL) + UZKLogInfo("URL %{public}@ doesn't yet exist", fileURL); } UZKLogDebug("Initializing private fields"); @@ -335,6 +311,9 @@ + (BOOL)pathIsAZip:(NSString *)filePath UZKLogDebug("File is not a ZIP. Unknown contents in 3rd and 4th bytes (%02X %02X)", dataBytes[2], dataBytes[3]); } + @catch (NSException *e) { + UZKLogError("Error checking if %{public}@ is a Zip archive: %{public}@", filePath, e); + } @finally { [handle closeFile]; } @@ -395,17 +374,23 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL BOOL success = [self performActionWithArchiveOpen:^(NSError * __autoreleasing*innerError) { UZKCreateActivity("Finding File Info Items"); + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting listFileInfo:error: action"); + return; + } + UZKLogInfo("Getting global info..."); - unzGoToNextFile(welf.unzFile); + unzGoToNextFile(sself.unzFile); unz_global_info gi; - int err = unzGetGlobalInfo(welf.unzFile, &gi); + int err = unzGetGlobalInfo(sself.unzFile, &gi); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error getting global info (%d)", @"UnzipKit", _resources, @"Detailed error string"), err]; UZKLogError("UZKErrorCodeArchiveNotFound: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeArchiveNotFound - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeArchiveNotFound + detail:detail]; return; } @@ -413,20 +398,20 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL UZKLogDebug("fileCount: %lu", (unsigned long)fileCount); UZKLogInfo("Going to first file..."); - err = unzGoToFirstFile(welf.unzFile); + err = unzGoToFirstFile(sself.unzFile); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error going to first file in archive (%d)", @"UnzipKit", _resources, @"Detailed error string"), err]; UZKLogError("UZKErrorCodeFileNavigationError: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileNavigationError - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileNavigationError + detail:detail]; return; } for (NSUInteger i = 0; i < fileCount; i++) { UZKLogDebug("Iterating through file info (iteration #%lu)", (unsigned long)i+1); - UZKFileInfo *info = [welf currentFileInZipInfo:innerError]; + UZKFileInfo *info = [sself currentFileInZipInfo:innerError]; if (info) { UZKLogDebug("Info found: %{public}@", info.filename); @@ -437,7 +422,7 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL } UZKLogDebug("Going to next file..."); - err = unzGoToNextFile(welf.unzFile); + err = unzGoToNextFile(sself.unzFile); if (err == UNZ_END_OF_LIST_OF_FILE) { UZKLogInfo("End of file found"); return; @@ -447,8 +432,8 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error navigating to next file (%d)", @"UnzipKit", _resources, @"Detailed error string"), err]; UZKLogError("UZKErrorCodeFileNavigationError: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileNavigationError - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileNavigationError + detail:detail]; return; } } @@ -468,20 +453,6 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL - (BOOL)extractFilesTo:(NSString *)destinationDirectory overwrite:(BOOL)overwrite error:(NSError * __autoreleasing*)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self extractFilesTo:destinationDirectory - overwrite:overwrite - progress:nil - error:error]; -#pragma clang diagnostic pop -} - -- (BOOL)extractFilesTo:(NSString *)destinationDirectory - overwrite:(BOOL)overwrite - progress:(void (^)(UZKFileInfo *currentFile, CGFloat percentArchiveDecompressed))progressBlock - error:(NSError * __autoreleasing*)error { UZKCreateActivity("Extracting Files to Directory"); @@ -515,7 +486,12 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory UZKCreateActivity("Performing Extraction"); NSError *strongError = nil; - + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting extractFilesTo:overwrite:error: action"); + return; + } + @try { for (UZKFileInfo *info in fileInfo) { UZKLogDebug("Extracting %{public}@ to disk", info.filename); @@ -524,22 +500,18 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error locating file '%@' in archive", @"UnzipKit", _resources, @"Detailed error string"), info.filename]; UZKLogError("Halted file extraction due to user cancellation: %{public}@", detail); - [welf assignError:&strongError code:UZKErrorCodeUserCancelled - detail:detail]; + [sself assignError:&strongError code:UZKErrorCodeUserCancelled + detail:detail]; return; } @autoreleasepool { - if (progressBlock) { - progressBlock(info, bytesDecompressed / totalSize.doubleValue); - } - if (![self locateFileInZip:info.filename error:&strongError]) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error locating file '%@' in archive", @"UnzipKit", _resources, @"Detailed error string"), info.filename]; UZKLogError("UZKErrorCodeFileNotFoundInArchive: %{public}@", detail); - [welf assignError:&strongError code:UZKErrorCodeFileNotFoundInArchive - detail:detail]; + [sself assignError:&strongError code:UZKErrorCodeFileNotFoundInArchive + detail:detail]; return; } @@ -563,14 +535,14 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Failed to create destination directory: %@", @"UnzipKit", _resources, @"Detailed error string"), extractDir]; UZKLogError("UZKErrorCodeOutputError: %{public}@", detail); - [welf assignError:&strongError code:UZKErrorCodeOutputError - detail:detail]; + [sself assignError:&strongError code:UZKErrorCodeOutputError + detail:detail]; return; } } if (info.isDirectory) { - UZKLogDebug("Created empty directory") + UZKLogDebug("Created empty directory"); continue; } @@ -588,11 +560,11 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory attributes:nil]; if (!createSuccess) { - NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error creating current file (%d) '%@'", @"UnzipKit", _resources, @"Detailed error string"), + NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error creating current file (%@) '%@'", @"UnzipKit", _resources, @"Detailed error string"), strongError, info.filename]; UZKLogError("UZKErrorCodeOutputError: %{public}@", detail); - [welf assignError:&strongError code:UZKErrorCodeOutputError - detail:detail]; + [sself assignError:&strongError code:UZKErrorCodeOutputError + detail:detail]; return; } @@ -605,23 +577,24 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error writing to file: %@", @"UnzipKit", _resources, @"Detailed error string"), deflatedFileURL]; UZKLogError("UZKErrorCodeOutputError: %{public}@", detail); - [welf assignError:&strongError code:UZKErrorCodeOutputError - detail:detail]; + [sself assignError:&strongError code:UZKErrorCodeOutputError + detail:detail]; return; } + [progress becomeCurrentWithPendingUnitCount:info.uncompressedSize]; + UZKLogDebug("Extracting buffered data"); - BOOL extractSuccess = [welf extractBufferedDataFromFile:info.filename - error:&strongError - action: - ^(NSData *dataChunk, CGFloat percentDecompressed) { - UZKLogDebug("Writing data chunk of size %lu (%lld total so far)", (unsigned long)dataChunk.length, bytesDecompressed); - bytesDecompressed += dataChunk.length; - [deflatedFileHandle writeData:dataChunk]; - if (progressBlock) { - progressBlock(info, (double)bytesDecompressed / totalSize.doubleValue); - } - }]; + BOOL extractSuccess = [sself extractBufferedDataFromFile:info.filename + error:&strongError + action: + ^(NSData *dataChunk, CGFloat percentDecompressed) { + UZKLogDebug("Writing data chunk of size %lu (%lld total so far)", (unsigned long)dataChunk.length, bytesDecompressed); + bytesDecompressed += dataChunk.length; + [deflatedFileHandle writeData:dataChunk]; + }]; + + [progress resignCurrent]; UZKLogDebug("Closing file handle"); [deflatedFileHandle closeFile]; @@ -645,7 +618,6 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory forKey:NSProgressFileCompletedCountKey]; [progress setUserInfoObject:@(fileInfo.count) forKey:NSProgressFileTotalCountKey]; - progress.completedUnitCount = bytesDecompressed; } } } @@ -670,29 +642,8 @@ - (nullable NSData *)extractData:(UZKFileInfo *)fileInfo error:error]; } -- (nullable NSData *)extractData:(UZKFileInfo *)fileInfo - progress:(void (^)(CGFloat))progress - error:(NSError * __autoreleasing*)error -{ - return [self extractDataFromFile:fileInfo.filename - progress:progress - error:error]; -} - - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:(NSError * __autoreleasing *)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self extractDataFromFile:filePath - progress:nil - error:error]; -#pragma clang diagnostic pop -} - -- (nullable NSData *)extractDataFromFile:(NSString *)filePath - progress:(void (^)(CGFloat))progressBlock - error:(NSError * __autoreleasing*)error { UZKCreateActivity("Extracting Data from File"); @@ -705,19 +656,9 @@ - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:&extractError action:^(NSData *dataChunk, CGFloat percentDecompressed) { UZKLogDebug("Appending data chunk of size %lu (%.3f%% complete)", (unsigned long)dataChunk.length, (double)percentDecompressed * 100); - - if (progressBlock) { - progressBlock(percentDecompressed); - } - [result appendData:dataChunk]; }]; - if (progressBlock) { - UZKLogDebug("Declaring extraction progress as completed"); - progressBlock(1.0); - } - if (success) { return [NSData dataWithData:result]; } @@ -800,20 +741,26 @@ - (BOOL)performOnDataInArchive:(void (^)(UZKFileInfo *, NSData *, BOOL *))action return [self performOnFilesInArchive:^(UZKFileInfo *fileInfo, BOOL *stop) { UZKLogInfo("Locating file %{public}@", fileInfo.filename); - if (![welf locateFileInZip:fileInfo.filename error:error]) { + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting performOnDataInArchive:error: action"); + return; + } + + if (![sself locateFileInZip:fileInfo.filename error:error]) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Failed to locate '%@' in archive during-perform on-data operation", @"UnzipKit", _resources, @"Detailed error string"), fileInfo.filename]; UZKLogError("UZKErrorCodeFileNotFoundInArchive: %{public}@", detail); - [welf assignError:error code:UZKErrorCodeFileNotFoundInArchive - detail:detail]; + [sself assignError:error code:UZKErrorCodeFileNotFoundInArchive + detail:detail]; return; } UZKLogInfo("Reading file from archive"); - NSData *fileData = [welf readFile:fileInfo.filename - length:fileInfo.uncompressedSize - error:error]; + NSData *fileData = [sself readFile:fileInfo.filename + length:fileInfo.uncompressedSize + error:error]; if (!fileData) { UZKLogError("Error reading file %{public}@ in archive", fileInfo.filename); @@ -837,17 +784,23 @@ - (BOOL)extractBufferedDataFromFile:(NSString *)filePath NSUInteger bufferSize = 1024 * 256; // 256 kb, arbitrary BOOL success = [self performActionWithArchiveOpen:^(NSError * __autoreleasing*innerError) { - if (![welf locateFileInZip:filePath error:innerError]) { + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting extractBufferedDataFromFile:error:action: action"); + return; + } + + if (![sself locateFileInZip:filePath error:innerError]) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Failed to locate '%@' in archive during buffered read", @"UnzipKit", _resources, @"Detailed error string"), filePath]; UZKLogError("UZKErrorCodeFileNotFoundInArchive: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileNotFoundInArchive - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileNotFoundInArchive + detail:detail]; return; } UZKLogInfo("Getting file info"); - UZKFileInfo *info = [welf currentFileInZipInfo:innerError]; + UZKFileInfo *info = [sself currentFileInZipInfo:innerError]; if (!info) { UZKLogError("Failed to get info of file %{public}@ in archive", filePath); @@ -857,7 +810,7 @@ - (BOOL)extractBufferedDataFromFile:(NSString *)filePath progress.totalUnitCount = info.uncompressedSize; UZKLogInfo("Opening file"); - if (![welf openFile:innerError]) { + if (![sself openFile:innerError]) { UZKLogError("Failed to open file %{public}@ in archive", filePath); return; } @@ -876,14 +829,14 @@ - (BOOL)extractBufferedDataFromFile:(NSString *)filePath @autoreleasepool { UZKLogDebug("Reading file data"); NSMutableData *data = [NSMutableData dataWithLength:bufferSize]; - int bytesRead = unzReadCurrentFile(welf.unzFile, data.mutableBytes, (unsigned)bufferSize); + int bytesRead = unzReadCurrentFile(sself.unzFile, data.mutableBytes, (unsigned)bufferSize); if (bytesRead < 0) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Failed to read file %@ in zip", @"UnzipKit", _resources, @"Detailed error string"), info.filename]; UZKLogError("Error reading data (code %d): %{public}@", bytesRead, detail); - [welf assignError:&strongInnerError code:bytesRead - detail:detail]; + [sself assignError:&strongInnerError code:bytesRead + detail:detail]; break; } else if (bytesRead == 0) { @@ -911,7 +864,7 @@ - (BOOL)extractBufferedDataFromFile:(NSString *)filePath } UZKLogInfo("Closing file..."); - int err = unzCloseCurrentFile(welf.unzFile); + int err = unzCloseCurrentFile(sself.unzFile); if (err != UNZ_OK) { if (err == UZKErrorCodeCRCError) { err = UZKErrorCodeInvalidPassword; @@ -919,8 +872,8 @@ - (BOOL)extractBufferedDataFromFile:(NSString *)filePath NSString *detail = NSLocalizedStringFromTableInBundle(@"Error closing current file during buffered read", @"UnzipKit", _resources, @"Detailed error string"); UZKLogError("Error closing file (code %d): %{public}@", err, detail); - [welf assignError:innerError code:err - detail:detail]; + [sself assignError:innerError code:err + detail:detail]; return; } @@ -1032,7 +985,7 @@ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath if (extractedCRC != fileInfo.CRC) { UZKLogError("CRC mismatch in '%{public}@': expected %010lu, found %010lu", - fileInfo.filename, (unsigned long)fileInfo.CRC, extractedCRC) + fileInfo.filename, (uLong)fileInfo.CRC, extractedCRC); dataIsValid = NO; } @@ -1058,47 +1011,13 @@ - (BOOL)writeData:(NSData *)data error:(NSError * __autoreleasing*)error { return [self writeData:data - filePath:filePath - fileDate:nil - compressionMethod:UZKCompressionMethodDefault - password:nil - overwrite:YES - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - progress:(void (^)(CGFloat percentCompressed))progress - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:nil - compressionMethod:UZKCompressionMethodDefault - password:nil - overwrite:YES - progress:progress - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:UZKCompressionMethodDefault - password:nil - overwrite:YES + props:[[ZipFileProperties alloc] init:filePath] error:error]; } - (BOOL)writeData:(NSData *)data filePath:(NSString *)filePath fileDate:(NSDate *)fileDate - progress:(void (^)(CGFloat percentCompressed))progress error:(NSError * __autoreleasing*)error { return [self writeData:data @@ -1106,23 +1025,6 @@ - (BOOL)writeData:(NSData *)data fileDate:fileDate compressionMethod:UZKCompressionMethodDefault password:nil - overwrite:YES - progress:progress - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(NSString *)password - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:method - password:password overwrite:YES error:error]; } @@ -1132,16 +1034,15 @@ - (BOOL)writeData:(NSData *)data fileDate:(NSDate *)fileDate compressionMethod:(UZKCompressionMethod)method password:(NSString *)password - progress:(void (^)(CGFloat percentCompressed))progress error:(NSError * __autoreleasing*)error { + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + props.password = password; + return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:method - password:password - overwrite:YES - progress:progress + props:props error:error]; } @@ -1153,78 +1054,46 @@ - (BOOL)writeData:(NSData *)data overwrite:(BOOL)overwrite error:(NSError * __autoreleasing*)error { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + props.password = password; + props.overwriteIfInArchive = overwrite; + return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:method - password:password - overwrite:overwrite - progress:nil + props:props error:error]; -#pragma clang diagnostic pop } - (BOOL)writeData:(NSData *)data filePath:(NSString *)filePath fileDate:(NSDate *)fileDate + posixPermissions:(short)permissions compressionMethod:(UZKCompressionMethod)method password:(NSString *)password overwrite:(BOOL)overwrite - progress:(void (^)(CGFloat percentCompressed))progressBlock error:(NSError * __autoreleasing*)error { - return [self writeData:data - filePath:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:method - password:password - overwrite:overwrite - progress:progressBlock - error:error]; -} + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.permissions = permissions; + props.compressionMethod = method; + props.password = password; + props.overwriteIfInArchive = overwrite; -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - posixPermissions:(short)permissions -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - error:(NSError * __autoreleasing*)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self writeData:data - filePath:filePath - fileDate:fileDate - posixPermissions:permissions - compressionMethod:method - password:password - overwrite:overwrite - progress:nil + props:props error:error]; -#pragma clang diagnostic pop } - (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate - posixPermissions:(short)permissions -compressionMethod:(UZKCompressionMethod)method - password:(NSString *)password - overwrite:(BOOL)overwrite - progress:(void (^)(CGFloat percentCompressed))progressBlock + props:(ZipFileProperties *)props error:(NSError * __autoreleasing*)error { UZKCreateActivity("Writing Data"); - UZKLogInfo("Writing data to archive. filePath: %{public}@, fileDate: %{time_t}ld, compressionMethod: %ld, password: %{public}@, " - "overwrite: %{public}@, progress block specified: %{public}@, error pointer specified: %{public}@", - filePath, lrint(fileDate.timeIntervalSince1970), (long)method, password != nil ? @"" : @"(null)", overwrite ? @"YES" : @"NO", - progressBlock ? @"YES" : @"NO", error ? @"YES" : @"NO"); + UZKLogInfo("Writing data to archive. Properties: %{public}@, error pointer specified: %{public}@", + props, error ? @"YES" : @"NO"); const NSUInteger bufferSize = 4096; //Arbitrary const void *bytes = data.bytes; @@ -1232,18 +1101,20 @@ - (BOOL)writeData:(NSData *)data NSProgress *progress = [self beginProgressOperation:data.length]; progress.cancellable = NO; - if (progressBlock) { - UZKLogDebug("Calling progress block with zero"); - progressBlock(0); - } - __weak UZKArchive *welf = self; uLong calculatedCRC = crc32(0, data.bytes, (uInt)data.length); + props.crc = calculatedCRC; UZKLogDebug("Calculated CRC: %010lu", calculatedCRC); BOOL success = [self performWriteAction:^int(uLong *crc, NSError * __autoreleasing*innerError) { UZKCreateActivity("Performing File Write"); + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting writeData:props:error: action"); + return ZIP_BADZIPFILE; + } + NSAssert(crc, @"No CRC reference passed", nil); *crc = calculatedCRC; @@ -1254,7 +1125,7 @@ - (BOOL)writeData:(NSData *)data unsigned int dataRemaining = (unsigned int)(data.length - i); unsigned int size = (unsigned int)(dataRemaining < bufferSize ? dataRemaining : bufferSize); - int err = zipWriteInFileInZip(welf.zipFile, (const char *)bytes + i, size); + int err = zipWriteInFileInZip(sself.zipFile, (const char *)bytes + i, size); if (err != ZIP_OK) { UZKLogError("Error writing data: %d", err); @@ -1262,39 +1133,21 @@ - (BOOL)writeData:(NSData *)data } progress.completedUnitCount += size; - - if (progressBlock) { - double percentComplete = i / (double)data.length; - UZKLogDebug("Calling progress block at %.3f%%", percentComplete * 100); - progressBlock(percentComplete); - } } return ZIP_OK; } - filePath:filePath - fileDate:fileDate - posixPermissions:permissions - compressionMethod:method - password:password - overwrite:overwrite - CRC:calculatedCRC + props:props error:error]; return success; } -- (BOOL)writeIntoBuffer:(NSString *)filePath - error:(NSError * __autoreleasing*)error - block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError * __autoreleasing*actionError))action +- (BOOL)writeIntoBufferAtPath:(NSString *)filePath + error:(NSError * __autoreleasing*)error + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError * __autoreleasing*actionError))action { - return [self writeIntoBuffer:filePath - fileDate:nil - posixPermissions:0 - compressionMethod:UZKCompressionMethodDefault - overwrite:YES - CRC:0 - password:nil + return [self writeIntoBuffer:[[ZipFileProperties alloc] init:filePath] error:error block:action]; } @@ -1304,15 +1157,10 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath error:(NSError * __autoreleasing*)error block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError * __autoreleasing*actionError))action { - return [self writeIntoBuffer:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:UZKCompressionMethodDefault - overwrite:YES - CRC:0 - password:nil - error:error - block:action]; + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + + return [self writeIntoBuffer:props error:error block:action]; } - (BOOL)writeIntoBuffer:(NSString *)filePath @@ -1321,15 +1169,11 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath error:(NSError * __autoreleasing*)error block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError * __autoreleasing*actionError))action { - return [self writeIntoBuffer:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:method - overwrite:YES - CRC:0 - password:nil - error:error - block:action]; + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + + return [self writeIntoBuffer:props error:error block:action]; } - (BOOL)writeIntoBuffer:(NSString *)filePath @@ -1339,15 +1183,12 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath error:(NSError * __autoreleasing*)error block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError * __autoreleasing*actionError))action { - return [self writeIntoBuffer:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:method - overwrite:overwrite - CRC:0 - password:nil - error:error - block:action]; + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + props.overwriteIfInArchive = overwrite; + + return [self writeIntoBuffer:props error:error block:action]; } - (BOOL)writeIntoBuffer:(NSString *)filePath @@ -1358,15 +1199,13 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath error:(NSError *__autoreleasing *)error block:(BOOL (^)(BOOL (^)(const void *, unsigned int), NSError *__autoreleasing *))action { - return [self writeIntoBuffer:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:method - overwrite:overwrite - CRC:preCRC - password:nil - error:error - block:action]; + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + props.overwriteIfInArchive = overwrite; + props.crc = preCRC; + + return [self writeIntoBuffer:props error:error block:action]; } - (BOOL)writeIntoBuffer:(NSString *)filePath @@ -1378,15 +1217,14 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath error:(NSError *__autoreleasing *)error block:(BOOL (^)(BOOL (^)(const void *, unsigned int), NSError *__autoreleasing *))action { - return [self writeIntoBuffer:filePath - fileDate:fileDate - posixPermissions:0 - compressionMethod:method - overwrite:overwrite - CRC:preCRC - password:password - error:error - block:action]; + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.compressionMethod = method; + props.overwriteIfInArchive = overwrite; + props.crc = preCRC; + props.password = password; + + return [self writeIntoBuffer:props error:error block:action]; } - (BOOL)writeIntoBuffer:(NSString *)filePath @@ -1398,15 +1236,30 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath password:(NSString *)password error:(NSError *__autoreleasing *)error block:(BOOL (^)(BOOL (^)(const void *, unsigned int), NSError *__autoreleasing *))action +{ + ZipFileProperties *props = [[ZipFileProperties alloc] init:filePath]; + props.timestamp = fileDate; + props.permissions = permissions; + props.compressionMethod = method; + props.overwriteIfInArchive = overwrite; + props.crc = preCRC; + props.password = password; + + return [self writeIntoBuffer:props error:error block:action]; +} + +- (BOOL)writeIntoBuffer:(ZipFileProperties *)props + error:(NSError *__autoreleasing *)error + block:(BOOL (^)(BOOL (^)(const void *, unsigned int), NSError *__autoreleasing *))action { UZKCreateActivity("Writing Into Buffer"); - UZKLogInfo("Writing data into buffer. filePath: %{public}@, fileDate: %{time_t}ld, compressionMethod: %ld, " - "overwrite: %{public}@, CRC: %010lu, password: %{public}@, error pointer specified: %{public}@", - filePath, lrint(fileDate.timeIntervalSince1970), (long)method, overwrite ? @"YES" : @"NO", preCRC, - password != nil ? @"" : @"(null)", error ? @"YES" : @"NO"); + UZKLogInfo("Writing data into buffer. Properties: %{public}@, error pointer specified: %{public}@", + props, error ? @"YES" : @"NO"); + + uLong preCRC = props.crc; - NSAssert(preCRC != 0 || ([password length] == 0 && [self.password length] == 0), + NSAssert(preCRC != 0 || ([props.password length] == 0 && [self.password length] == 0), @"Cannot provide a password when writing into a buffer, " "unless a CRC is provided up front for inclusion in the header", nil); @@ -1415,6 +1268,12 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath BOOL success = [self performWriteAction:^int(uLong *crc, NSError * __autoreleasing*innerError) { UZKCreateActivity("Performing File Write"); + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting writeIntoBuffer:error:block: action"); + return ZIP_BADZIPFILE; + } + NSAssert(crc, @"No CRC reference passed", nil); if (!action) { @@ -1445,19 +1304,13 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath NSLocalizedStringFromTableInBundle(@"Incorrect CRC provided\n%@ given\n%@ calculated", @"UnzipKit", _resources, @"CRC mismatch error detail"), preCRCStr, calculatedCRCStr]; UZKLogError("UZKErrorCodePreCRCMismatch: %{public}@", detail); - return [welf assignError:innerError code:UZKErrorCodePreCRCMismatch - detail:detail]; + return [sself assignError:innerError code:UZKErrorCodePreCRCMismatch + detail:detail]; } return result; } - filePath:filePath - fileDate:fileDate - posixPermissions:permissions - compressionMethod:method - password:password - overwrite:overwrite - CRC:preCRC + props:props error:error]; return success; @@ -1607,9 +1460,9 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error detail:detail]; } - UZKLogDebug("Allocating commentary"); - char *commentary = (char*)malloc(unzipInfo.size_file_comment); - if ((commentary == NULL) && (unzipInfo.size_file_comment != 0)) { + UZKLogDebug("Allocating file comment buffer"); + char *fileCommentBuffer = unzipInfo.size_file_comment > 0 ? (char*)malloc(unzipInfo.size_file_comment) : NULL; + if ((fileCommentBuffer == NULL) && (unzipInfo.size_file_comment != 0)) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error allocating commentary info of %@ while deleting %@", @"UnzipKit", _resources, @"Detailed error string"), currentFileName, filePath]; UZKLogError("UZKErrorCodeDeleteFile: %{public}@", detail); @@ -1623,14 +1476,14 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error } UZKLogDebug("Getting file info"); - err = unzGetCurrentFileInfo64(source_zip, &unzipInfo, filename_inzip, FILE_IN_ZIP_MAX_NAME_LENGTH, extra_field, unzipInfo.size_file_extra, commentary, unzipInfo.size_file_comment); + err = unzGetCurrentFileInfo64(source_zip, &unzipInfo, filename_inzip, FILE_IN_ZIP_MAX_NAME_LENGTH, extra_field, unzipInfo.size_file_extra, fileCommentBuffer, unzipInfo.size_file_comment); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error reading extra_field and commentary info of %@ while deleting %@ (%d)", @"UnzipKit", _resources, @"Detailed error string"), currentFileName, filePath, err]; UZKLogError("UZKErrorCodeDeleteFile: %{public}@", detail); UZKLogDebug("Closing source_zip, dest_zip, freeing global_comment, extra_field, commentary"); free(extra_field); - free(commentary); + free(fileCommentBuffer); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; } @@ -1650,7 +1503,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; } @@ -1658,7 +1511,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error UZKLogDebug("Getting local extra field size"); int size_local_extra = unzGetLocalExtrafield(source_zip, NULL, 0); if (size_local_extra < 0) { - NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error getting size_local_extra for file while deleting %@", @"UnzipKit", _resources, @"Detailed error string"), + NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error getting size_local_extra for file while deleting %@ from archive %@", @"UnzipKit", _resources, @"Detailed error string"), currentFileName, filePath]; UZKLogError("UZKErrorCodeDeleteFile: %{public}@", detail); UZKLogDebug("Closing source_zip, dest_zip, freeing global_comment, extra_field, commentary"); @@ -1666,7 +1519,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; } @@ -1682,7 +1535,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; } @@ -1697,7 +1550,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; @@ -1715,7 +1568,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); return [self assignError:error code:UZKErrorCodeDeleteFile detail:detail]; @@ -1733,7 +1586,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); return [self assignError:error code:UZKErrorCodeDeleteFile @@ -1751,7 +1604,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error UZKLogDebug("Opening file in destination archive"); err = zipOpenNewFileInZip2(dest_zip, filename_inzip, &zipInfo, - local_extra, size_local_extra, extra_field, (uInt)unzipInfo.size_file_extra, commentary, + local_extra, size_local_extra, extra_field, (uInt)unzipInfo.size_file_extra, fileCommentBuffer, method, level, 1); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error opening %@ in destination zip while deleting %@ (%d)", @"UnzipKit", _resources, @"Detailed error string"), @@ -1762,7 +1615,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); return [self assignError:error code:UZKErrorCodeDeleteFile @@ -1781,7 +1634,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); return [self assignError:error code:UZKErrorCodeDeleteFile @@ -1790,7 +1643,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error // Close destination archive UZKLogDebug("Closing file in destination archive"); - err = zipCloseFileInZipRaw64(dest_zip, unzipInfo.uncompressed_size, unzipInfo.crc); + err = zipCloseFileInZipRaw64(dest_zip, unzipInfo.uncompressed_size, (uLong)unzipInfo.crc); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error closing %@ in destination zip while deleting %@ (%d)", @"UnzipKit", _resources, @"Detailed error string"), currentFileName, filePath, err]; @@ -1800,7 +1653,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); return [self assignError:error code:UZKErrorCodeDeleteFile @@ -1819,7 +1672,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error unzClose(source_zip); free(global_comment); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); return [self assignError:error code:UZKErrorCodeDeleteFile @@ -1828,7 +1681,7 @@ - (BOOL)deleteFile:(NSString *)filePath error:(NSError * __autoreleasing*)error UZKLogDebug("Freeing extra_field, commentary, local_extra, buf"); free(extra_field); - free(commentary); + free(fileCommentBuffer); free(local_extra); free(buf); @@ -1952,6 +1805,8 @@ - (BOOL)performActionWithArchiveOpen:(void(^)(NSError * __autoreleasing*innerErr NSError *openError = nil; NSError *actionError = nil; + BOOL shouldCloseFile = YES; + @try { if (![self openFile:self.filename inMode:mode @@ -1964,6 +1819,7 @@ - (BOOL)performActionWithArchiveOpen:(void(^)(NSError * __autoreleasing*innerErr *error = openError; } + shouldCloseFile = openError.code != UZKErrorCodeFileWrite; return NO; } @@ -1973,15 +1829,17 @@ - (BOOL)performActionWithArchiveOpen:(void(^)(NSError * __autoreleasing*innerErr } } @finally { - NSError *closeError = nil; - if (![self closeFile:&closeError inMode:mode]) { - UZKLogDebug("Archive failed to close"); - - if (error && !actionError && !openError) { - *error = closeError; + if (shouldCloseFile) { + NSError *closeError = nil; + if (![self closeFile:&closeError inMode:mode]) { + UZKLogDebug("Archive failed to close"); + + if (error && !actionError && !openError) { + *error = closeError; + } + + return NO; } - - return NO; } } @@ -1994,19 +1852,15 @@ - (BOOL)performActionWithArchiveOpen:(void(^)(NSError * __autoreleasing*innerErr } - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerError))write - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate - posixPermissions:(short)permissions - compressionMethod:(UZKCompressionMethod)method - password:(NSString *)password - overwrite:(BOOL)overwrite - CRC:(uLong)crc + props:(ZipFileProperties *)props error:(NSError * __autoreleasing*)error { UZKCreateActivity("Performing Write"); - if (overwrite) { - UZKLogInfo("Overwriting %{public}@ if it already exists. Will look for existing file to delete", filePath); + NSString *password = props.password; + + if (props.overwriteIfInArchive) { + UZKLogInfo("Overwriting %{public}@ if it already exists. Will look for existing file to delete", props.fullFilePath); NSError *listFilesError = nil; NSArray *existingFiles; @@ -2016,11 +1870,11 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr existingFiles = [self listFileInfo:&listFilesError]; } - if (existingFiles) { - UZKLogDebug("Existing files found. Looking for matches to filePath %{public}@", filePath); + if ([existingFiles count]) { + UZKLogDebug("Existing files found. Looking for matches to filePath %{public}@", props.fullFilePath); NSIndexSet *matchingFiles = [existingFiles indexesOfObjectsPassingTest: ^BOOL(UZKFileInfo *info, NSUInteger idx, BOOL *stop) { - if ([info.filename isEqualToString:filePath]) { + if ([info.filename isEqualToString:props.fullFilePath]) { *stop = YES; return YES; } @@ -2028,16 +1882,16 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr return NO; }]; - if (matchingFiles.count > 0 && ![self deleteFile:filePath error:error]) { - UZKLogError("Failed to delete %{public}@ before writing new data for it", filePath); + if (matchingFiles.count > 0 && ![self deleteFile:props.fullFilePath error:error]) { + UZKLogError("Failed to delete %{public}@ before writing new data for it", props.fullFilePath); return NO; } } } if (!password) { - UZKLogDebug("No password specified for file. Using archive's password: %{public}@", password != nil ? @"" : @"(null)"); password = self.password; + UZKLogDebug("No password specified for file. Using archive's password: %{public}@", password != nil ? @"" : @"(null)"); } __weak UZKArchive *welf = self; @@ -2045,9 +1899,15 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr BOOL success = [self performActionWithArchiveOpen:^(NSError * __autoreleasing*innerError) { UZKCreateActivity("Performing Write Action"); - UZKLogDebug("Making zip_fileinfo struct for date %{time_t}ld", lrint(fileDate.timeIntervalSince1970)); - zip_fileinfo zi = [UZKArchive zipFileInfoForDate:fileDate - posixPermissions:permissions]; + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting performWriteAction:props:error: action"); + return; + } + + UZKLogDebug("Making zip_fileinfo struct for date %{time_t}ld", lrint(props.timestamp.timeIntervalSince1970)); + zip_fileinfo zi = [UZKArchive zipFileInfoForDate:props.timestamp + posixPermissions:props.permissions]; const char *passwordStr = NULL; @@ -2057,23 +1917,23 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr } UZKLogDebug("Opening new file..."); - int err = zipOpenNewFileInZip3(welf.zipFile, - filePath.UTF8String, + int err = zipOpenNewFileInZip3(sself.zipFile, + props.fullFilePath.UTF8String, &zi, NULL, 0, NULL, 0, NULL, - (method != UZKCompressionMethodNone) ? Z_DEFLATED : 0, - method, + (props.compressionMethod != UZKCompressionMethodNone) ? Z_DEFLATED : 0, + props.compressionMethod, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, passwordStr, - crc); + props.crc); if (err != ZIP_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error opening file '%@' for write (%d)", @"UnzipKit", _resources, @"Detailed error string"), - filePath, err]; + props.fullFilePath, err]; UZKLogError("UZKErrorCodeFileOpenForWrite: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileOpenForWrite - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileOpenForWrite + detail:detail]; return; } @@ -2082,10 +1942,10 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr err = write(&outCRC, innerError); if (err < 0) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error writing to file '%@' (%d)", @"UnzipKit", _resources, @"Detailed error string"), - filePath, err]; + props.fullFilePath, err]; UZKLogError("UZKErrorCodeFileOpenForWrite: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileWrite - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileWrite + detail:detail]; return; } @@ -2093,10 +1953,10 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr err = zipCloseFileInZipRaw(self.zipFile, 0, outCRC); if (err != ZIP_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error closing file '%@' for write (%d)", @"UnzipKit", _resources, @"Detailed error string"), - filePath, err]; + props.fullFilePath, err]; UZKLogError("UZKErrorCodeFileOpenForWrite: %{public}@", detail); - [welf assignError:innerError code:UZKErrorCodeFileWrite - detail:detail]; + [sself assignError:innerError code:UZKErrorCodeFileWrite + detail:detail]; return; } @@ -2111,7 +1971,6 @@ - (BOOL)openFile:(NSString *)zipFile error:(NSError * __autoreleasing*)error { UZKCreateActivity("Opening File"); - UZKLogDebug("Opening file in mode %lu", (unsigned long)mode); if (error) { @@ -2169,7 +2028,7 @@ - (BOOL)openFile:(NSString *)zipFile } UZKLogDebug("Opening file for read..."); - self.unzFile = unzOpen(self.filename.UTF8String); + self.unzFile = unzOpen64(self.filename.UTF8String); if (self.unzFile == NULL) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error opening zip file %@", @"UnzipKit", _resources, @"Detailed error string"), zipFile]; @@ -2488,8 +2347,9 @@ - (NSString *)readGlobalComment { UZKLogDebug("Checking archive exists"); + NSURL *fileURL = self.fileURL; NSError *checkExistsError = nil; - if (![self.fileURL checkResourceIsReachableAndReturnError:&checkExistsError]) { + if (!fileURL || ![fileURL checkResourceIsReachableAndReturnError:&checkExistsError]) { UZKLogDebug("Archive not found"); return nil; } @@ -2501,17 +2361,23 @@ - (NSString *)readGlobalComment { BOOL success = [self performActionWithArchiveOpen:^(NSError * __autoreleasing*innerError) { UZKCreateActivity("Perform Action"); + __strong UZKArchive *sself = welf; + if (!sself) { + UZKLogInfo("UZKArchive instance has been deallocated. Exiting readGlobalComment action"); + return; + } + UZKLogDebug("Getting global info..."); unz_global_info global_info; - int err = unzGetGlobalInfo(welf.unzFile, &global_info); + int err = unzGetGlobalInfo(sself.unzFile, &global_info); if (err != UNZ_OK) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error getting global info of archive during comment read: %d", @"UnzipKit", _resources, @"Detailed error string"), err]; UZKLogError("UZKErrorCodeReadComment: %{public}@", detail); UZKLogDebug("Closing archive..."); - unzClose(welf.unzFile); + unzClose(sself.unzFile); - [welf assignError:innerError code:UZKErrorCodeReadComment detail:detail]; + [sself assignError:innerError code:UZKErrorCodeReadComment detail:detail]; return; } @@ -2525,21 +2391,21 @@ - (NSString *)readGlobalComment { NSString *detail = NSLocalizedStringFromTableInBundle(@"Error allocating the global comment during comment read", @"UnzipKit", _resources, @"Detailed error string"); UZKLogError("UZKErrorCodeReadComment: %{public}@", detail); UZKLogDebug("Closing archive..."); - unzClose(welf.unzFile); + unzClose(sself.unzFile); - [welf assignError:innerError code:UZKErrorCodeReadComment detail:detail]; + [sself assignError:innerError code:UZKErrorCodeReadComment detail:detail]; return; } UZKLogDebug("Reading global comment..."); - if ((unsigned int)unzGetGlobalComment(welf.unzFile, global_comment, global_info.size_comment + 1) != global_info.size_comment) { + if ((unsigned int)unzGetGlobalComment(sself.unzFile, global_comment, global_info.size_comment + 1) != global_info.size_comment) { NSString *detail = NSLocalizedStringFromTableInBundle(@"Error reading the comment (readGlobalComment)", @"UnzipKit", _resources, @"Detailed error string"); UZKLogError("UZKErrorCodeReadComment: %{public}@", detail); UZKLogDebug("Closing archive and freeing global_comment..."); - unzClose(welf.unzFile); + unzClose(sself.unzFile); free(global_comment); - [welf assignError:innerError code:UZKErrorCodeReadComment detail:@"Error reading global comment (unzGetGlobalComment)"]; + [sself assignError:innerError code:UZKErrorCodeReadComment detail:@"Error reading global comment (unzGetGlobalComment)"]; return; } @@ -2574,15 +2440,15 @@ - (BOOL)storeFileBookmark:(NSURL *)fileURL error:(NSError * __autoreleasing*)err relativeToURL:nil error:&bookmarkError]; - if (bookmarkError) { + if (!self.fileBookmark) { UZKLogFault("Error creating bookmark for URL %{public}@: %{public}@", fileURL, bookmarkError); } if (error) { - *error = bookmarkError ? bookmarkError : nil; + *error = bookmarkError; } - return bookmarkError == nil; + return self.fileBookmark != nil; } + (NSString *)figureOutCString:(const char *)filenameBytes diff --git a/Source/UZKFileInfo.h b/Source/UZKFileInfo.h index e14248e..898b67d 100644 --- a/Source/UZKFileInfo.h +++ b/Source/UZKFileInfo.h @@ -13,7 +13,7 @@ /** * Defines the various compression levels that can be applied to a file */ -typedef NS_ENUM(NSInteger, UZKCompressionMethod) { +typedef NS_ENUM(int, UZKCompressionMethod) { /** * Default level */ @@ -73,6 +73,11 @@ typedef NS_ENUM(NSInteger, UZKCompressionMethod) { */ @property (readonly) BOOL isDirectory; +/** + * YES if the file is a symbolic link + */ +@property (readonly) BOOL isSymbolicLink; + /** * The type of compression */ diff --git a/Source/UZKFileInfo.m b/Source/UZKFileInfo.m index 050adf6..4503313 100644 --- a/Source/UZKFileInfo.m +++ b/Source/UZKFileInfo.m @@ -7,6 +7,50 @@ #import "UZKFileInfo.h" #import "unzip.h" +/** + * Define the file storage system in zip according to "version made by" + * These values are taken from the PKWARE zip zpec, section 4.4.2.1 + * + * https://www.pkware.com/documents/casestudies/APPNOTE.TXT) + */ +typedef NS_ENUM(NSUInteger, UZKZipOS) { + UZKZipOSMSDOS = 0, + UZKZipOSAmiga = 1, + UZKZipOSOpenVMS = 2, + UZKZipOSUnix = 3, + UZKZipOSVMCMS = 4, + UZKZipOSAtariST = 5, + UZKZipOSOS2 = 6, + UZKZipOSClassicMac = 7, + UZKZipOSZSystem = 8, + UZKZipOSCPM = 9, + UZKZipOSWindowsNT = 10, + UZKZipOSMVS = 11, + UZKZipOSVSE = 12, + UZKZipOSAcorn = 13, + UZKZipOSVFAT = 14, + UZKZipOSAlternateMVS = 15, + UZKZipOSBeOS = 16, + UZKZipOSTandem = 17, + UZKZipOSOS400 = 18, + UZKZipOSDarwinOSX = 19 +}; + +/** + * These are the UNIX file types, as defined in system headers. These values are reused by the ZIP + * format's external file attributes (see especially Directory, Regular, SymLink) + */ +typedef NS_ENUM(ushort, UZKFileType) { + UZKFileTypeNamedPipe = S_IFIFO, + UZKFileTypeCharacterSpecial = S_IFCHR, + UZKFileTypeDirectory = S_IFDIR, + UZKFileTypeBlockSpecial = S_IFBLK, + UZKFileTypeRegular = S_IFREG, + UZKFileTypeSymLink = S_IFLNK, + UZKFileTypeSocket = S_IFSOCK +}; + + @interface UZKFileInfo () @property (readwrite) tm_unz zipTMUDate; @@ -19,6 +63,7 @@ @implementation UZKFileInfo @synthesize timestamp = _timestamp; + #pragma mark - Initialization @@ -35,7 +80,8 @@ - (instancetype)initWithFileInfo:(unz_file_info64 *)fileInfo filename:(NSString _zipTMUDate = fileInfo->tmu_date; _CRC = fileInfo->crc; _isEncryptedWithPassword = (fileInfo->flag & 1) != 0; - _isDirectory = [filename hasSuffix:@"/"]; + _isDirectory = [UZKFileInfo itemIsDirectory:fileInfo]; + _isSymbolicLink = [UZKFileInfo itemIsSymbolicLink:fileInfo]; if (_isDirectory) { _filename = [_filename substringToIndex:_filename.length - 1]; @@ -44,7 +90,7 @@ - (instancetype)initWithFileInfo:(unz_file_info64 *)fileInfo filename:(NSString _compressionMethod = [self readCompressionMethod:fileInfo->compression_method flag:fileInfo->flag]; - uLong permissions = (fileInfo->external_fa >> 16) & 0777U; + uLong permissions = [UZKFileInfo itemPermissions:fileInfo]; _posixPermissions = permissions ? permissions : 0644U; } return self; @@ -65,6 +111,41 @@ - (NSDate *)timestamp { +#pragma mark - Private Class Methods + + ++ (UZKZipOS)itemOS:(unz_file_info64 *)fileInfo { + return fileInfo->version >> 8; +} + ++ (UZKFileType)itemFileType:(unz_file_info64 *)fileInfo { + return S_IFMT & (fileInfo->external_fa >> 16); +} + ++ (uLong)itemPermissions:(unz_file_info64 *)fileInfo { + uLong permissionsMask = S_IRWXU | S_IRWXG | S_IRWXO; + return permissionsMask & (fileInfo->external_fa >> 16); +} + ++ (BOOL)itemIsDOSDirectory:(unz_file_info64 *)fileInfo { + return 0x01 & (fileInfo->external_fa >> 4); +} + ++ (BOOL)itemIsDirectory:(unz_file_info64 *)fileInfo { + UZKZipOS itemOS = [UZKFileInfo itemOS:fileInfo]; + if (itemOS == UZKZipOSMSDOS || itemOS == UZKZipOSWindowsNT) { + return [UZKFileInfo itemIsDOSDirectory:fileInfo]; + } + + return [UZKFileInfo itemFileType:fileInfo] == UZKFileTypeDirectory; +} + ++ (BOOL)itemIsSymbolicLink:(unz_file_info64 *)fileInfo { + return [UZKFileInfo itemFileType:fileInfo] == UZKFileTypeSymLink; +} + + + #pragma mark - Private Methods diff --git a/Source/UnzipKit.h b/Source/UnzipKit.h index d390a9d..8864a29 100644 --- a/Source/UnzipKit.h +++ b/Source/UnzipKit.h @@ -15,5 +15,5 @@ FOUNDATION_EXPORT double UnzipKitVersionNumber; FOUNDATION_EXPORT const unsigned char UnzipKitVersionString[]; -#import "UZKArchive.h" -#import "UZKFileInfo.h" +#import +#import diff --git a/Source/UnzipKitMacros.h b/Source/UnzipKitMacros.h index c68438d..1fa340e 100644 --- a/Source/UnzipKitMacros.h +++ b/Source/UnzipKitMacros.h @@ -48,25 +48,25 @@ extern BOOL unzipkitIsAtLeast10_13SDK; // Declared in UZKArchive.m minVersion.minorVersion = SDK_10_13_MINOR; \ minVersion.patchVersion = 0; \ unzipkitIsAtLeast10_13SDK = [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:minVersion]; \ - UZKLogDebug("Is >= 10.13 (or iOS 11): %@", unzipkitIsAtLeast10_13SDK ? @"YES" : @"NO"); + UZKLogDebug("Is >= 10.13 (or iOS 11): %@", unzipkitIsAtLeast10_13SDK ? @"YES" : @"NO") -#define UZKLog(format, ...) os_log(unzipkit_log, format, ##__VA_ARGS__); -#define UZKLogInfo(format, ...) os_log_info(unzipkit_log, format, ##__VA_ARGS__); -#define UZKLogDebug(format, ...) os_log_debug(unzipkit_log, format, ##__VA_ARGS__); +#define UZKLog(format, ...) os_log(unzipkit_log, format, ##__VA_ARGS__) +#define UZKLogInfo(format, ...) os_log_info(unzipkit_log, format, ##__VA_ARGS__) +#define UZKLogDebug(format, ...) os_log_debug(unzipkit_log, format, ##__VA_ARGS__) #define UZKLogError(format, ...) \ if (unzipkitIsAtLeast10_13SDK) os_log_error(unzipkit_log, format, ##__VA_ARGS__); \ - else os_log_with_type(unzipkit_log, OS_LOG_TYPE_ERROR, format, ##__VA_ARGS__); + else os_log_with_type(unzipkit_log, OS_LOG_TYPE_ERROR, format, ##__VA_ARGS__) #define UZKLogFault(format, ...) \ if (unzipkitIsAtLeast10_13SDK) os_log_fault(unzipkit_log, format, ##__VA_ARGS__); \ - else os_log_with_type(unzipkit_log, OS_LOG_TYPE_FAULT, format, ##__VA_ARGS__); + else os_log_with_type(unzipkit_log, OS_LOG_TYPE_FAULT, format, ##__VA_ARGS__) #define UZKCreateActivity(name) \ os_activity_t activity = os_activity_create(name, OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); \ - os_activity_scope(activity); + os_activity_scope(activity) #else // Fall back to regular NSLog diff --git a/Source/ZipFileProperties.swift b/Source/ZipFileProperties.swift new file mode 100644 index 0000000..8e8c4a2 --- /dev/null +++ b/Source/ZipFileProperties.swift @@ -0,0 +1,75 @@ +// +// ZipFileArgs.swift +// UnzipKit +// +// Created by Dov Frankel on 8/1/19. +// Copyright © 2019 Abbey Code. All rights reserved. +// + +import Foundation + +// Not a struct because of Objective-C compatibility +@objc public class ZipFileProperties: NSObject { + + + // MARK: - Required + + /// The full path and filename of the file to be written + @objc public var fullFilePath: String + + + // MARK: - Optional/Defaulted + + /// The UZKCompressionMethod to use (Default, None, Fastest, Best) + @objc public var compressionMethod = UZKCompressionMethod.default + + /** + The CRC-32 checksum for the data being written. Only required + if encrypting the file, otherwise it will be calculated automatically + */ + @objc public var crc: UInt = 0 + + /** + If true, and the file exists, delete it before writing. If false, append + the data into the archive without removing it first (legacy Objective-Zip + behavior). Turn this off to gain speed at the expense of creating bloated + archives. Defaults to true + */ + @objc public var overwriteIfInArchive = true + + /// Override the password associated with the rest of the archive (not a recommended practice) + @objc public var password: String? = nil + + /** + The desired POSIX permissions of the archived file (e.g. 0o644 in Swift + or 0644U in Objective-C). Defaults to 644 (Read/Write for owner, + read-only for group and others) + */ + @objc public var permissions: Int16 = 0o644 + + /// The timestamp of the file to be written + @objc public var timestamp: Date? + + + // MARK: - Initializer + + @objc public init(_ fullFilePath: String) { + self.fullFilePath = fullFilePath + } + + + // MARK: - Overrides + + public override var description: String { + let crcStr = String(crc, radix: 16).uppercased() + let password = self.password != nil ? "" : "none" + let permissionStr = String(permissions, radix: 8).uppercased() + + return """ + { fullFilePath: \(fullFilePath), compressionMethod: \(compressionMethod.rawValue), crc: \(crcStr), + overwriteIfInArchive: \(overwriteIfInArchive), password: \(password), + permissions: \(permissionStr), timestamp: \(timestamp?.description ?? "none") } + """.replacingOccurrences(of: "\n", with: " "); + } + +} diff --git a/Tests/CheckDataTests.m b/Tests/CheckDataTests.m index fe1f748..fceeb75 100644 --- a/Tests/CheckDataTests.m +++ b/Tests/CheckDataTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface CheckDataTests : UZKArchiveTestCase @end diff --git a/Tests/CommentsTests.m b/Tests/CommentsTests.m index 3a26b7e..90a7cd4 100644 --- a/Tests/CommentsTests.m +++ b/Tests/CommentsTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface CommentsTests : UZKArchiveTestCase @end diff --git a/Tests/DeleteFileTests.m b/Tests/DeleteFileTests.m index 61a3d04..02715a8 100644 --- a/Tests/DeleteFileTests.m +++ b/Tests/DeleteFileTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface DeleteFileTests : UZKArchiveTestCase @end @@ -108,11 +108,11 @@ - (void)testDeleteFile_ThirdFile NSArray *testArchives = @[@"Test Archive.zip", @"Test Archive (Password).zip"]; - NSSet *expectedFileSet = self.nonZipTestFilePaths; - NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSSet *expectedFileSet = self.nonZipTestFilePaths; + NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; NSString *fileToDelete = expectedFiles[2]; - NSMutableArray *newFileList = [NSMutableArray arrayWithArray:expectedFiles]; + NSMutableArray *newFileList = [NSMutableArray arrayWithArray:expectedFiles]; [newFileList removeObject:fileToDelete]; for (NSString *testArchiveName in testArchives) { diff --git a/Tests/ErrorHandlingTests.m b/Tests/ErrorHandlingTests.m index 8f4d783..eb48188 100644 --- a/Tests/ErrorHandlingTests.m +++ b/Tests/ErrorHandlingTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface ErrorHandlingTests : UZKArchiveTestCase @end diff --git a/Tests/ExtractBufferedDataTests.m b/Tests/ExtractBufferedDataTests.m index d341e01..b96d15f 100644 --- a/Tests/ExtractBufferedDataTests.m +++ b/Tests/ExtractBufferedDataTests.m @@ -6,29 +6,15 @@ // Copyright (c) 2015 Abbey Code. All rights reserved. // -#import "UnzipKit.h" +@import UnzipKit; + +@import os.log; +@import os.signpost; #import "UZKArchiveTestCase.h" #import "UnzipKitMacros.h" -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 -#import -enum SignPostCode: uint { // Use to reference in Instruments (http://stackoverflow.com/a/39416673/105717) - SignPostCodeCreateTextFile = 0, - SignPostCodeArchiveData = 1, - SignPostCodeExtractData = 2, -}; - -enum SignPostColor: uint { // standard color scheme for signposts in Instruments - SignPostColorBlue = 0, - SignPostColorGreen = 1, - SignPostColorPurple = 2, - SignPostColorOrange = 3, - SignPostColorRed = 4, -}; -#endif - @interface ExtractBufferedDataTests : UZKArchiveTestCase @end @@ -66,16 +52,20 @@ - (void)testExtractBufferedData #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 - (void)testExtractBufferedData_VeryLarge { - kdebug_signpost_start(SignPostCodeCreateTextFile, 0, 0, 0, SignPostColorBlue); + os_log_t log = os_log_create("UnzipKit-testExtractBufferedData_VeryLarge", OS_LOG_CATEGORY_POINTS_OF_INTEREST); + + os_signpost_id_t createTextFileID = os_signpost_id_generate(log); + os_signpost_interval_begin(log, createTextFileID, "Create Text File", "Start"); NSURL *largeTextFile = [self emptyTextFileOfLength:100000000]; // Increase for a more dramatic test XCTAssertNotNil(largeTextFile, @"No large text file URL returned"); - kdebug_signpost_end(SignPostCodeCreateTextFile, 0, 0, 0, SignPostColorBlue); - - kdebug_signpost_start(SignPostCodeArchiveData, 0, 0, 0, SignPostColorGreen); + os_signpost_interval_end(log, createTextFileID, "Create Text File", "End"); + + os_signpost_id_t archiveDataID = os_signpost_id_generate(log); + os_signpost_interval_begin(log, archiveDataID, "Archive Data", "Start"); NSURL *archiveURL = [self archiveWithFiles:@[largeTextFile]]; XCTAssertNotNil(archiveURL, @"No archived large text file URL returned"); - kdebug_signpost_end(SignPostCodeArchiveData, 0, 0, 0, SignPostColorGreen); - + os_signpost_interval_end(log, archiveDataID, "Archive Data", "End"); + NSURL *deflatedFileURL = [self.tempDirectory URLByAppendingPathComponent:@"DeflatedTextFile.txt"]; BOOL createSuccess = [[NSFileManager defaultManager] createFileAtPath:deflatedFileURL.path contents:nil @@ -89,8 +79,9 @@ - (void)testExtractBufferedData_VeryLarge UZKArchive *archive = [[UZKArchive alloc] initWithURL:archiveURL error:nil]; - kdebug_signpost_start(SignPostCodeExtractData, 0, 0, 0, SignPostColorPurple); - + os_signpost_id_t extractDataID = os_signpost_id_generate(log); + os_signpost_interval_begin(log, extractDataID, "Extract Data", "Start"); + NSError *error = nil; BOOL success = [archive extractBufferedDataFromFile:largeTextFile.lastPathComponent error:&error @@ -100,10 +91,11 @@ - (void)testExtractBufferedData_VeryLarge UZKLogDebug("Decompressed: %f%%", percentDecompressed); # endif [deflated writeData:dataChunk]; + os_signpost_event_emit(log, extractDataID, "Extracted chunk", "%f", percentDecompressed); }]; - kdebug_signpost_end(SignPostCodeExtractData, 0, 0, 0, SignPostColorPurple); - + os_signpost_interval_end(log, extractDataID, "Extract Data", "End"); + XCTAssertTrue(success, @"Failed to read buffered data"); XCTAssertNil(error, @"Error reading buffered data"); diff --git a/Tests/ExtractDataTests.m b/Tests/ExtractDataTests.m index 3c71d27..3b2a193 100644 --- a/Tests/ExtractDataTests.m +++ b/Tests/ExtractDataTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; #import "UnzipKitMacros.h" @interface ExtractDataTests : UZKArchiveTestCase diff --git a/Tests/FileDescriptorUsageTests.m b/Tests/FileDescriptorUsageTests.m index a824da7..d750105 100644 --- a/Tests/FileDescriptorUsageTests.m +++ b/Tests/FileDescriptorUsageTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface FileDescriptorUsageTests : UZKArchiveTestCase @end @@ -143,6 +143,72 @@ - (void)testFileDescriptorUsage_WriteIntoArchive NSString *fileContents = [NSString stringWithFormat:@"This is a string %d", x]; NSData *newFileData = [fileContents dataUsingEncoding:NSUTF8StringEncoding]; NSString *fileName = fileList.lastObject; + BOOL writeResult = [archive writeData:newFileData + filePath:fileName + error:&writeError]; + XCTAssertTrue(writeResult, @"Failed to write to archive (attempt %d)", x); + XCTAssertNil(writeError, @"Error writing to archive (attempt %d)", x); + + NSError *extractError = nil; + NSData *extractedData = [archive extractDataFromFile:fileName + error:&extractError]; + XCTAssertEqualObjects(extractedData, newFileData, @"Incorrect data written to file (attempt %d)", x); + XCTAssertNil(extractError, @"Error extracting from archive (attempt %d)", x); + } + } + + NSInteger finalFileCount = [self numberOfOpenFileHandles]; + + XCTAssertEqualWithAccuracy(initialFileCount, finalFileCount, 5, @"File descriptors were left open"); +} + + +- (void)testFileDescriptorUsage_WriteIntoArchive_deprecatedOverload +{ + NSInteger initialFileCount = [self numberOfOpenFileHandles]; + + NSURL *testArchiveOriginalURL = [self largeArchive]; + NSString *testArchiveName = testArchiveOriginalURL.lastPathComponent; + NSFileManager *fm = [NSFileManager defaultManager]; + + for (NSInteger i = 0; i < 100; i++) { + // Keep this test from stalling out the build + printf("testFileDescriptorUsage_WriteIntoArchive: Iteration %ld/100\n", (long)i); + + NSString *tempDir = [self randomDirectoryName]; + NSURL *tempDirURL = [self.tempDirectory URLByAppendingPathComponent:tempDir]; + NSURL *testArchiveCopyURL = [tempDirURL URLByAppendingPathComponent:testArchiveName]; + + NSError *error = nil; + [fm createDirectoryAtURL:tempDirURL + withIntermediateDirectories:YES + attributes:nil + error:&error]; + + XCTAssertNil(error, @"Error creating temp directory: %@", tempDirURL); + + [fm copyItemAtURL:testArchiveOriginalURL toURL:testArchiveCopyURL error:&error]; + XCTAssertNil(error, @"Error copying test archive \n from: %@ \n\n to: %@", testArchiveOriginalURL, testArchiveCopyURL); + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveCopyURL error:nil]; + + NSArray *fileList = [archive listFilenames:&error]; + XCTAssertNotNil(fileList, @"No filenames listed"); + + for (NSString *fileName in fileList) { + NSData *fileData = [archive extractDataFromFile:fileName + error:&error]; + XCTAssertNotNil(fileData, @"No data extracted"); + XCTAssertNil(error, @"Error extracting data"); + } + + for (int x = 0; x < 50; x++) { + NSError *writeError = nil; + NSString *fileContents = [NSString stringWithFormat:@"This is a string %d", x]; + NSData *newFileData = [fileContents dataUsingEncoding:NSUTF8StringEncoding]; + NSString *fileName = fileList.lastObject; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL writeResult = [archive writeData:newFileData filePath:fileName fileDate:[NSDate date] @@ -150,6 +216,7 @@ - (void)testFileDescriptorUsage_WriteIntoArchive password:nil overwrite:YES error:&writeError]; +#pragma clang diagnostic pop XCTAssertTrue(writeResult, @"Failed to write to archive (attempt %d)", x); XCTAssertNil(writeError, @"Error writing to archive (attempt %d)", x); diff --git a/Tests/FileInfoTests.swift b/Tests/FileInfoTests.swift new file mode 100644 index 0000000..2ff5324 --- /dev/null +++ b/Tests/FileInfoTests.swift @@ -0,0 +1,143 @@ +// +// FileInfoTests.swift +// UnzipKitTests +// +// Created by Dov Frankel on 7/25/19. +// Copyright © 2019 Abbey Code. All rights reserved. +// + +import XCTest + +class FileInfoTests: UZKArchiveTestCase { + + func testIsDirectory_NoDirectories() { + let testArchiveName = "Test Archive.zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [false, false, false] + let actual = fileInfo.map { $0.isDirectory } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsDirectories() { + let testArchiveName = "Test Archive (Directories).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + "Folder A": true, + "Folder A/Test File A.txt": false, + "Test File B.txt": false, + "X Folder": true, + "X Folder/Test File C.txt": false + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isDirectory + } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsDirectories_DOS() { + let testArchiveName = "Test Archive (DOS Directories).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + "FOLDERA": true, + "FOLDERA/TESTFILE.TXT": false, + "TESTFILE.TXT": false, + "XFOLDER": true, + "XFOLDER/TESTFILE.TXT": false + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isDirectory + } + + XCTAssertEqual(actual, expected) + } + + func testIsSymbolicLink_NoSymLinks() { + let testArchiveName = "Test Archive.zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [false, false, false] + let actual = fileInfo.map { $0.isSymbolicLink } + + XCTAssertEqual(actual, expected) + } + + #if os(OSX) + func testIsSymbolicLink_ContainsSymLinkFile() { + let textFileURL = self.emptyTextFile(ofLength: 20)! + let symLinkURL = textFileURL.deletingLastPathComponent() + .appendingPathComponent(textFileURL + .lastPathComponent + .replacingOccurrences(of: ".txt", with: "-Link.txt")) + + try! FileManager.default.createSymbolicLink(at: symLinkURL, withDestinationURL: textFileURL) + + let archiveURL = self.archive(withFiles: [textFileURL, symLinkURL], + zipOptions: ["--junk-paths", "--symlinks"])! + let archive = try! UZKArchive(url: archiveURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + textFileURL.lastPathComponent: false, + symLinkURL.lastPathComponent: true, + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isSymbolicLink + } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsSymLinkFileAndDir() { + struct FileInfo: Equatable, CustomStringConvertible { + var description: String { + return "FileInfo(isLink: \(isLink), isDir: \(isDir))" + } + + let isLink: Bool + let isDir: Bool + + static func == (lhs: FileInfo, rhs: FileInfo) -> Bool { + return lhs.isLink == rhs.isLink && lhs.isLink == rhs.isLink + } + } + + let testArchiveName = "Test Archive (SymLink Directory).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected: Dictionary = [ + "testDir": FileInfo(isLink: false, isDir: true), + "testDir/testFile2.md": FileInfo(isLink: false, isDir: false), + "testDirLink": FileInfo(isLink: true, isDir: false), + "testFile.md": FileInfo(isLink: false, isDir: false), + "testFileLink.md": FileInfo(isLink: true, isDir: false) + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename!] = FileInfo(isLink: $1.isSymbolicLink, isDir: $1.isDirectory) + } + + XCTAssertEqual(actual, expected) + } + #endif + +} diff --git a/Tests/ListFileInfoTests.m b/Tests/ListFileInfoTests.m index e3fcb7a..4d66005 100644 --- a/Tests/ListFileInfoTests.m +++ b/Tests/ListFileInfoTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface ListFileInfoTests : UZKArchiveTestCase @end @@ -61,7 +61,7 @@ - (void)testListFileInfo { XCTAssertEqual(fileInfo.uncompressedSize, expectedFileSize, @"Incorrect uncompressed file size"); // Test Compression method - UZKCompressionMethod expectedCompressionMethod = ((NSNumber *)expectedCompressionMethods[fileInfo.filename]).integerValue; + UZKCompressionMethod expectedCompressionMethod = ((NSNumber *)expectedCompressionMethods[fileInfo.filename]).intValue; XCTAssertEqual(fileInfo.compressionMethod, expectedCompressionMethod, @"Incorrect compression method"); } } diff --git a/Tests/ListFilenamesTests.m b/Tests/ListFilenamesTests.m index 77eca2b..42b2322 100644 --- a/Tests/ListFilenamesTests.m +++ b/Tests/ListFilenamesTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface ListFilenamesTests : UZKArchiveTestCase @end diff --git a/Tests/ModesTests.m b/Tests/ModesTests.m index 9d739bc..098f35f 100644 --- a/Tests/ModesTests.m +++ b/Tests/ModesTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface ModesTests : UZKArchiveTestCase @@ -72,9 +72,9 @@ - (void)testModes_ReadWhileWriting UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; NSError *writeError = nil; - [archive writeIntoBuffer:@"newFile.zip" - error:&writeError - block: + [archive writeIntoBufferAtPath:@"newFile.zip" + error:&writeError + block: ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { NSError *readError = nil; [archive listFilenames:&readError]; @@ -93,15 +93,15 @@ - (void)testModes_NestedWrites UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; NSError *outerWriteError = nil; - [archive writeIntoBuffer:@"newFile.zip" - error:&outerWriteError - block: + [archive writeIntoBufferAtPath:@"outer file.txt" + error:&outerWriteError + block: ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { NSError *innerWriteError = nil; - [archive writeIntoBuffer:@"newFile.zip" - error:&innerWriteError - block:^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) {return YES;}]; - XCTAssertNotNil(innerWriteError, @"Nested write operation succeeded"); + [archive writeIntoBufferAtPath:@"inner file.txt" + error:&innerWriteError + block:^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) {return YES;}]; + XCTAssertNotNil(innerWriteError, @"Nested write operation succeeded"); XCTAssertEqual(innerWriteError.code, UZKErrorCodeFileWrite, @"Wrong error code returned"); return YES; diff --git a/Tests/MultithreadingTests.m b/Tests/MultithreadingTests.m index 8e11988..bce0df5 100644 --- a/Tests/MultithreadingTests.m +++ b/Tests/MultithreadingTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; #import "UnzipKitMacros.h" @interface MultithreadingTests : UZKArchiveTestCase diff --git a/Tests/PasswordProtectionTests.m b/Tests/PasswordProtectionTests.m index e942d98..0221997 100644 --- a/Tests/PasswordProtectionTests.m +++ b/Tests/PasswordProtectionTests.m @@ -7,7 +7,8 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; + @interface PasswordProtectionTests : UZKArchiveTestCase @end @@ -49,6 +50,44 @@ - (void)testIsPasswordProtected_PasswordRequired_LastFileOnly password = @"111111"; } + ZipFileProperties *props = [[ZipFileProperties alloc] init:testFile]; + props.password = password; + BOOL result = [writeArchive writeData:fileData + props:props + error:&writeError]; + + XCTAssertTrue(result, @"Error writing archive data"); + XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); + }]; + + UZKArchive *readArchive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + XCTAssertTrue(readArchive.isPasswordProtected, @"isPasswordProtected = NO for password-protected archive"); +} + +- (void)testIsPasswordProtected_PasswordRequired_LastFileOnly_deprecatedOverload +{ + NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; + + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"testIsPasswordProtected_PasswordRequired_LastFileOnly.zip"]; + + UZKArchive *writeArchive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + __block NSError *writeError = nil; + + [testFiles enumerateObjectsUsingBlock:^(NSString *testFile, NSUInteger idx, BOOL *stop) { + NSData *fileData = [NSData dataWithContentsOfURL:self.testFileURLs[testFile]]; + [testFileData addObject:fileData]; + + NSString *password = nil; + + if (idx == testFiles.count - 1) { + password = @"111111"; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL result = [writeArchive writeData:fileData filePath:testFile fileDate:nil @@ -59,7 +98,8 @@ - (void)testIsPasswordProtected_PasswordRequired_LastFileOnly XCTAssertTrue(result, @"Error writing archive data"); XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); }]; - +#pragma clang diagnostic pop + UZKArchive *readArchive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; XCTAssertTrue(readArchive.isPasswordProtected, @"isPasswordProtected = NO for password-protected archive"); diff --git a/Tests/PerformOnDataTests.m b/Tests/PerformOnDataTests.m index c631458..1b4f5e3 100644 --- a/Tests/PerformOnDataTests.m +++ b/Tests/PerformOnDataTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface PerformOnDataTests : UZKArchiveTestCase @end diff --git a/Tests/PerformOnFilesTests.m b/Tests/PerformOnFilesTests.m index d07da3c..f0038f0 100644 --- a/Tests/PerformOnFilesTests.m +++ b/Tests/PerformOnFilesTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface PerformOnFilesTests : UZKArchiveTestCase @end @@ -21,7 +21,7 @@ - (void)testPerformOnFiles @"Test Archive (Password).zip"]; NSSet *expectedFileSet = self.nonZipTestFilePaths; - NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; for (NSString *testArchiveName in testArchives) { NSURL *testArchiveURL = self.testFileURLs[testArchiveName]; diff --git a/Tests/PermissionsTests.swift b/Tests/PermissionsTests.swift index 7910068..e6cd55b 100644 --- a/Tests/PermissionsTests.swift +++ b/Tests/PermissionsTests.swift @@ -7,6 +7,7 @@ // import XCTest +import UnzipKit class PermissionsTests: UZKArchiveTestCase { @@ -93,7 +94,7 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteData_Default() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -112,7 +113,7 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteData_NonDefault() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -120,8 +121,9 @@ class PermissionsTests: UZKArchiveTestCase { let expectedPermissions: Int16 = 0o742 - try! writeArchive.write(testFileData, filePath: testFilename, fileDate: nil, posixPermissions: expectedPermissions, - compressionMethod: .default, password: nil, overwrite: true) + let props = ZipFileProperties(testFilename) + props.permissions = expectedPermissions; + try! writeArchive.write(testFileData, props: props) let readArchive = try! UZKArchive(url: testArchiveURL) let fileList = try! readArchive.listFileInfo() @@ -131,18 +133,41 @@ class PermissionsTests: UZKArchiveTestCase { XCTAssertEqual(actualPermissions, expectedPermissions) } + + func testWriteData_NonDefault_deprecatedOverload() { + let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteData.zip") + let testFilename = nonZipTestFilePaths.first! + let testFileURL = testFileURLs[testFilename] as! URL + let testFileData = try! Data(contentsOf: testFileURL) + + let writeArchive = try! UZKArchive(url: testArchiveURL) + + let expectedPermissions: Int16 = 0o742 + + try! writeArchive.deprecatedWrite(testFileData, filePath: testFilename, fileDate: nil, posixPermissions: expectedPermissions, + compressionMethod: .default, password: nil, overwrite: true) + let readArchive = try! UZKArchive(url: testArchiveURL) + let fileList = try! readArchive.listFileInfo() + + let writtenFileInfo = fileList.first { $0.filename == testFilename } + let actualPermissions = writtenFileInfo!.posixPermissions + + XCTAssertEqual(actualPermissions, expectedPermissions) + } + func testWriteIntoBuffer_Default() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteBufferedData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) let writeArchive = try! UZKArchive(url: testArchiveURL) - try! writeArchive.write(intoBuffer: testFilename) { (writeDataHandler, error) in - testFileData.withUnsafeBytes({ buffer in - writeDataHandler(buffer, UInt32(testFileData.count)) - }) + try! writeArchive.writeIntoBuffer(atPath: testFilename) { (writeDataHandler, error) in + let buffer = testFileData.withUnsafeBytes { + $0.baseAddress?.assumingMemoryBound(to: UInt32.self) + } + return writeDataHandler(buffer!, UInt32(testFileData.count)) } let readArchive = try! UZKArchive(url: testArchiveURL) @@ -156,19 +181,49 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteIntoBuffer_NonDefault() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteBufferedData_CustomPermissions.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! + let testFileURL = testFileURLs[testFilename] as! URL + let testFileData = try! Data(contentsOf: testFileURL) + + let expectedPermissions: Int16 = 0o764 + + let writeArchive = try! UZKArchive(url: testArchiveURL) + let props = ZipFileProperties(testFilename) + props.permissions = expectedPermissions + props.overwriteIfInArchive = false + try! writeArchive.write(intoBuffer: props) + { (writeDataHandler, error) in + let buffer = testFileData.withUnsafeBytes { + $0.baseAddress?.assumingMemoryBound(to: UInt32.self) + } + return writeDataHandler(buffer!, UInt32(testFileData.count)) + } + + let readArchive = try! UZKArchive(url: testArchiveURL) + let fileList = try! readArchive.listFileInfo() + + let writtenFileInfo = fileList.first { $0.filename == testFilename } + let actualPermissions = writtenFileInfo!.posixPermissions + + XCTAssertEqual(actualPermissions, expectedPermissions) + } + + func testWriteIntoBuffer_NonDefault_deprecatedOverload() { + let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteBufferedData_CustomPermissions.zip") + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) let expectedPermissions: Int16 = 0o764 let writeArchive = try! UZKArchive(url: testArchiveURL) - try! writeArchive.write(intoBuffer: testFilename, fileDate: nil, posixPermissions: expectedPermissions, - compressionMethod: .default, overwrite: false, crc: 0, password: nil) + try! writeArchive.deprecatedWrite(intoBuffer: testFilename, fileDate: nil, posixPermissions: expectedPermissions, + compressionMethod: .default, overwrite: false, crc: 0, password: nil) { (writeDataHandler, error) in - testFileData.withUnsafeBytes({ buffer in - writeDataHandler(buffer, UInt32(testFileData.count)) - }) + let buffer = testFileData.withUnsafeBytes { + $0.baseAddress?.assumingMemoryBound(to: UInt32.self) + } + return writeDataHandler(buffer!, UInt32(testFileData.count)) } let readArchive = try! UZKArchive(url: testArchiveURL) diff --git a/Tests/ProgressReportingTests.m b/Tests/ProgressReportingTests.m index 7db7a28..87a0ba0 100644 --- a/Tests/ProgressReportingTests.m +++ b/Tests/ProgressReportingTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +#import #import "UnzipKitMacros.h" @interface ProgressReportingTests : UZKArchiveTestCase @@ -87,6 +87,111 @@ - (void)testProgressReporting_ExtractFiles_FractionCompleted } } +#if !TARGET_OS_IPHONE +- (void)testProgressReporting_ExtractFiles_FractionCompleted_LargeFile +{ + NSURL *largeTextFile = [self emptyTextFileOfLength:1000000]; + NSURL *testArchiveURL = [self archiveWithFiles:@[largeTextFile]]; + + NSString *extractDirectory = [self randomDirectoryWithPrefix:@"LargeFileProgress"]; + NSURL *extractURL = [self.tempDirectory URLByAppendingPathComponent:extractDirectory]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSProgress *extractFilesProgress = [NSProgress progressWithTotalUnitCount:1]; + [extractFilesProgress becomeCurrentWithPendingUnitCount:1]; + + NSString *observedSelector = NSStringFromSelector(@selector(fractionCompleted)); + + [extractFilesProgress addObserver:self + forKeyPath:observedSelector + options:NSKeyValueObservingOptionInitial + context:ExtractFilesContext]; + + NSError *extractError = nil; + BOOL success = [archive extractFilesTo:extractURL.path + overwrite:NO + error:&extractError]; + + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertTrue(success, @"Archive failed to extract large file to %@", extractURL); + + [extractFilesProgress resignCurrent]; + [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; + + XCTAssertEqualWithAccuracy(extractFilesProgress.fractionCompleted, 1.00, .0000000001, @"Progress never reported as completed"); + + NSUInteger expectedProgressUpdates = 5; + NSArray *expectedProgresses = @[@0, + @0.262144, + @0.524287, + @0.786431, + @1.0]; + + XCTAssertEqual(self.fractionsCompletedReported.count, expectedProgressUpdates, @"Incorrect number of progress updates"); + for (NSUInteger i = 0; i < expectedProgressUpdates; i++) { + float expectedProgress = expectedProgresses[i].floatValue; + float actualProgress = self.fractionsCompletedReported[i].floatValue; + + XCTAssertEqualWithAccuracy(actualProgress, expectedProgress, 0.00001f, @"Incorrect progress reported at index %ld", (long)i); + } +} + +- (void)testProgressReporting_ExtractFiles_FractionCompleted_TwoLargeFiles +{ + NSArray *largeTextFiles = @[ + [self emptyTextFileOfLength:1000000], + [self emptyTextFileOfLength:500000], + ]; + NSURL *testArchiveURL = [self archiveWithFiles:largeTextFiles]; + + NSString *extractDirectory = [self randomDirectoryWithPrefix:@"TwoLargeFilesProgress"]; + NSURL *extractURL = [self.tempDirectory URLByAppendingPathComponent:extractDirectory]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSProgress *extractFilesProgress = [NSProgress progressWithTotalUnitCount:1]; + [extractFilesProgress becomeCurrentWithPendingUnitCount:1]; + + NSString *observedSelector = NSStringFromSelector(@selector(fractionCompleted)); + + [extractFilesProgress addObserver:self + forKeyPath:observedSelector + options:NSKeyValueObservingOptionInitial + context:ExtractFilesContext]; + + NSError *extractError = nil; + BOOL success = [archive extractFilesTo:extractURL.path + overwrite:NO + error:&extractError]; + + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertTrue(success, @"Archive failed to extract large file to %@", extractURL); + + [extractFilesProgress resignCurrent]; + [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; + + XCTAssertEqualWithAccuracy(extractFilesProgress.fractionCompleted, 1.00, .0000000001, @"Progress never reported as completed"); + + NSUInteger expectedProgressUpdates = 7; + NSArray *expectedProgresses = @[@0, + @0.174762, + @0.349525, + @0.524287, + @0.666667, + @0.841429, + @1.0]; + + XCTAssertEqual(self.fractionsCompletedReported.count, expectedProgressUpdates, @"Incorrect number of progress updates"); + for (NSUInteger i = 0; i < expectedProgressUpdates; i++) { + float expectedProgress = expectedProgresses[i].floatValue; + float actualProgress = self.fractionsCompletedReported[i].floatValue; + + XCTAssertEqualWithAccuracy(actualProgress, expectedProgress, 0.00001f, @"Incorrect progress reported at index %ld", (long)i); + } +} +#endif + - (void)testProgressReporting_ExtractFiles_Description { NSString *testArchiveName = @"Test Archive.zip"; @@ -568,10 +673,9 @@ - (void)observeValueForKeyPath:(NSString *)keyPath { observerCallCount++; - NSProgress *progress; + NSProgress *progress = object; - if ([object isKindOfClass:[NSProgress class]]) { - progress = object; + if ([progress isKindOfClass:[NSProgress class]]) { [self.fractionsCompletedReported addObject:@(progress.fractionCompleted)]; } else { return; diff --git a/Tests/PropertyTests.m b/Tests/PropertyTests.m index 136545a..8d5de5a 100644 --- a/Tests/PropertyTests.m +++ b/Tests/PropertyTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface PropertyTests : UZKArchiveTestCase @end diff --git a/Tests/Test Data/Test Archive (DOS Directories).zip b/Tests/Test Data/Test Archive (DOS Directories).zip new file mode 100644 index 0000000..99efd91 Binary files /dev/null and b/Tests/Test Data/Test Archive (DOS Directories).zip differ diff --git a/Tests/Test Data/Test Archive (Directories).zip b/Tests/Test Data/Test Archive (Directories).zip new file mode 100644 index 0000000..4ccadbc Binary files /dev/null and b/Tests/Test Data/Test Archive (Directories).zip differ diff --git a/Tests/Test Data/Test Archive (SymLink Directory).zip b/Tests/Test Data/Test Archive (SymLink Directory).zip new file mode 100644 index 0000000..14d535e Binary files /dev/null and b/Tests/Test Data/Test Archive (SymLink Directory).zip differ diff --git a/Tests/Test Plans/Complete.xctestplan b/Tests/Test Plans/Complete.xctestplan new file mode 100644 index 0000000..3091bd7 --- /dev/null +++ b/Tests/Test Plans/Complete.xctestplan @@ -0,0 +1,30 @@ +{ + "configurations" : [ + { + "id" : "441D8E09-AFA9-48CB-A3B7-386CFF9C42C8", + "name" : "Basic", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : false, + "targetForVariableExpansion" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA659D1A40AEAE00685B6D", + "name" : "UnzipKit" + } + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA65A81A40AEAE00685B6D", + "name" : "UnzipKitTests" + } + } + ], + "version" : 1 +} diff --git a/Tests/Test Plans/Fast.xctestplan b/Tests/Test Plans/Fast.xctestplan new file mode 100644 index 0000000..baf7584 --- /dev/null +++ b/Tests/Test Plans/Fast.xctestplan @@ -0,0 +1,39 @@ +{ + "configurations" : [ + { + "id" : "441D8E09-AFA9-48CB-A3B7-386CFF9C42C8", + "name" : "Configuration", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : false, + "targetForVariableExpansion" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA659D1A40AEAE00685B6D", + "name" : "UnzipKit" + }, + "testExecutionOrdering" : "random" + }, + "testTargets" : [ + { + "parallelizable" : true, + "skippedTests" : [ + "ExtractFilesTests\/testExtractZip64_LargeFile", + "ExtractFilesTests\/testExtractZip64_ManyFiles", + "FileDescriptorUsageTests\/testFileDescriptorUsage", + "FileDescriptorUsageTests\/testFileDescriptorUsage_ExtractInsidePerformOnFiles", + "FileDescriptorUsageTests\/testFileDescriptorUsage_WriteIntoArchive", + "FileDescriptorUsageTests\/testFileDescriptorUsage_WriteIntoArchive_deprecatedOverload" + ], + "target" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA65A81A40AEAE00685B6D", + "name" : "UnzipKitTests" + } + } + ], + "version" : 1 +} diff --git a/Tests/Test Plans/Rigorous.xctestplan b/Tests/Test Plans/Rigorous.xctestplan new file mode 100644 index 0000000..0d72a8a --- /dev/null +++ b/Tests/Test Plans/Rigorous.xctestplan @@ -0,0 +1,61 @@ +{ + "configurations" : [ + { + "id" : "441D8E09-AFA9-48CB-A3B7-386CFF9C42C8", + "name" : "Address Sanitizer", + "options" : { + "addressSanitizer" : { + "enabled" : true + }, + "mallocScribbleEnabled" : true, + "nsZombieEnabled" : true + } + }, + { + "id" : "6ED785ED-44DD-40FD-A8EA-8C4DD8E6C51D", + "name" : "Thread Sanitizer", + "options" : { + "threadSanitizerEnabled" : true, + "undefinedBehaviorSanitizerEnabled" : true + } + }, + { + "id" : "CD341A45-58D9-4997-A06F-4FD6124BCD58", + "name" : "Malloc Checks", + "options" : { + "environmentVariableEntries" : [ + { + "key" : "MALLOC_PERMIT_INSANE_REQUESTS", + "value" : "1" + } + ], + "guardMallocEnabled" : true, + "mallocGuardEdgesEnabled" : true, + "mallocScribbleEnabled" : true, + "mallocStackLoggingOptions" : { + "loggingType" : "allAllocations" + }, + "nsZombieEnabled" : true + } + } + ], + "defaultOptions" : { + "codeCoverage" : false, + "targetForVariableExpansion" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA659D1A40AEAE00685B6D", + "name" : "UnzipKit" + } + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:UnzipKit.xcodeproj", + "identifier" : "96EA65A81A40AEAE00685B6D", + "name" : "UnzipKitTests" + } + } + ], + "version" : 1 +} diff --git a/Tests/UZKArchive+Deprecated.h b/Tests/UZKArchive+Deprecated.h new file mode 100644 index 0000000..72a41ee --- /dev/null +++ b/Tests/UZKArchive+Deprecated.h @@ -0,0 +1,53 @@ +// +// UZKArchive+Deprecated.h +// UnzipKit +// +// Created by Dov Frankel on 8/12/19. +// Copyright © 2019 Abbey Code. All rights reserved. +// + +#import + +#import "UZKArchive.h" + +@interface UZKArchive (Deprecated) + +NS_ASSUME_NONNULL_BEGIN + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + error:(NSError **)error; + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + overwrite:(BOOL)overwrite + error:(NSError **)error; + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + posixPermissions:(short)permissions + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + overwrite:(BOOL)overwrite + error:(NSError **)error; + +- (BOOL)deprecatedWriteIntoBuffer:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + posixPermissions:(short)permissions + compressionMethod:(UZKCompressionMethod)method + overwrite:(BOOL)overwrite + CRC:(unsigned long)preCRC + password:(nullable NSString *)password + error:(NSError **)error + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Tests/UZKArchive+Deprecated.m b/Tests/UZKArchive+Deprecated.m new file mode 100644 index 0000000..cc0422d --- /dev/null +++ b/Tests/UZKArchive+Deprecated.m @@ -0,0 +1,97 @@ +// +// UZKArchive+Deprecated.m +// UnzipKit +// +// Created by Dov Frankel on 8/12/19. +// Copyright © 2019 Abbey Code. All rights reserved. +// + +#import "UZKArchive+Deprecated.h" +#import "UZKArchive.h" + +@implementation UZKArchive (Deprecated) + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + error:(NSError *__autoreleasing *)error +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [self writeData:data + filePath:filePath + fileDate:fileDate + compressionMethod:method + password:password + error:error]; +#pragma clang diagnostic pop +} + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + overwrite:(BOOL)overwrite + error:(NSError *__autoreleasing *)error +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [self writeData:data + filePath:filePath + fileDate:fileDate + compressionMethod:method + password:password + overwrite:overwrite + error:error]; +#pragma clang diagnostic pop +} + +- (BOOL)deprecatedWriteData:(NSData *)data + filePath:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + posixPermissions:(short)permissions + compressionMethod:(UZKCompressionMethod)method + password:(nullable NSString *)password + overwrite:(BOOL)overwrite + error:(NSError *__autoreleasing *)error +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [self writeData:data + filePath:filePath + fileDate:fileDate + posixPermissions:permissions + compressionMethod:method + password:password + overwrite:overwrite + error:error]; +#pragma clang diagnostic pop +} + +- (BOOL)deprecatedWriteIntoBuffer:(NSString *)filePath + fileDate:(nullable NSDate *)fileDate + posixPermissions:(short)permissions + compressionMethod:(UZKCompressionMethod)method + overwrite:(BOOL)overwrite + CRC:(unsigned long)preCRC + password:(nullable NSString *)password + error:(NSError *__autoreleasing *)error + block:(BOOL(^)(BOOL(^writeData)(const void *bytes, unsigned int length), NSError **actionError))action +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [self writeIntoBuffer:filePath + fileDate:fileDate + posixPermissions:permissions + compressionMethod:method + overwrite:overwrite + CRC:preCRC + password:password + error:error + block:action]; +#pragma clang diagnostic pop +} +@end diff --git a/Tests/UZKArchiveTestCase.h b/Tests/UZKArchiveTestCase.h index 1373471..b85420f 100644 --- a/Tests/UZKArchiveTestCase.h +++ b/Tests/UZKArchiveTestCase.h @@ -23,9 +23,9 @@ @property (retain) NSURL *tempDirectory; @property (retain) NSMutableDictionary *testFileURLs; -@property (retain) NSMutableDictionary *unicodeFileURLs; -@property (retain) NSSet *nonZipTestFilePaths; -@property (retain) NSSet *nonZipUnicodeFilePaths; +@property (retain) NSMutableDictionary *unicodeFileURLs; +@property (retain) NSSet *nonZipTestFilePaths; +@property (retain) NSSet *nonZipUnicodeFilePaths; @property (retain) NSURL *corruptArchive; @@ -44,6 +44,9 @@ - (NSInteger)numberOfOpenFileHandles; - (NSURL *)archiveWithFiles:(NSArray *)fileURLs; - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs zipOptions:(NSArray *)zipOpts; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs name:(NSString *)name zipOptions:(NSArray *)zipOpts; - (BOOL)extractArchive:(NSURL *)url password:(NSString *)password; - (NSURL *)largeArchive; #endif diff --git a/Tests/UZKArchiveTestCase.m b/Tests/UZKArchiveTestCase.m index 2cd9fe8..37fac77 100644 --- a/Tests/UZKArchiveTestCase.m +++ b/Tests/UZKArchiveTestCase.m @@ -15,7 +15,9 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundef" -#if UNIFIED_LOGGING_SUPPORTED +// UnzipKit compiles the library files into the test target directly, so these +// are defined inside UZKArchive.m there. Only necessary in Xcode project +#if defined(UNIFIED_LOGGING_SUPPORTED) && defined(CODE_IMPORTED_AS_LIB) os_log_t unzipkit_log; BOOL unzipkitIsAtLeast10_13SDK; #endif @@ -52,7 +54,10 @@ - (void)setUp { NSArray *testFiles = @[ @"Test Archive.zip", + @"Test Archive (DOS Directories).zip", + @"Test Archive (Directories).zip", @"Test Archive (Password).zip", + @"Test Archive (SymLink Directory).zip", @"L'incertain.zip", @"Aces.zip", @"Comments Archive.zip", @@ -233,19 +238,36 @@ - (NSInteger)numberOfOpenFileHandles { return result; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs { - return [self archiveWithFiles:fileURLs password:nil]; + return [self archiveWithFiles:fileURLs name:nil zipOptions:nil]; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password { - NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString]; - return [self archiveWithFiles:fileURLs password:password name:uniqueString]; + return [self archiveWithFiles:fileURLs password:password name:nil]; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs zipOptions:(NSArray *)zipOpts { + return [self archiveWithFiles:fileURLs name:nil zipOptions:zipOpts]; +} + +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name +{ + return [self archiveWithFiles:fileURLs name:name zipOptions:@[@"--junk-paths", @"--password", password]]; +} + +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs name:(NSString *)name zipOptions:(NSArray *)zipOpts; +{ + if (![name length]) { + name = [[NSProcessInfo processInfo] globallyUniqueString]; + } + + if (!zipOpts) { + zipOpts = @[@"--junk-paths"]; + } + NSURL *archiveURL = [[self.tempDirectory URLByAppendingPathComponent:name] URLByAppendingPathExtension:@"zip"]; NSFileHandle *consoleOutputHandle = nil; @@ -269,12 +291,8 @@ - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password na while (startIndex < filePaths.count) { @autoreleasepool { - NSMutableArray *zipArgs = [NSMutableArray arrayWithArray: - @[@"-j", archiveURL.path]]; - - if (password) { - [zipArgs addObjectsFromArray:@[@"-P", password]]; - } + NSMutableArray *zipArgs = [NSMutableArray arrayWithArray:zipOpts]; + [zipArgs addObject:archiveURL.path]; NSRange currentRange = NSMakeRange(startIndex, MIN(pathsRemaining, maxFilesPerCall)); NSArray *pathArrayChunk = [filePaths subarrayWithRange:currentRange]; @@ -288,6 +306,7 @@ - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password na UZKLog("Compressing files %lu-%lu of %lu", startIndex + 1, startIndex + pathArrayChunk.count, filePaths.count); + UZKLogDebug("Zip command: %@ %@", task.launchPath, [task.arguments componentsJoinedByString:@" "]); [task launch]; [task waitUntilExit]; @@ -345,8 +364,8 @@ - (NSURL *)largeArchive static NSInteger archiveNumber = 1; NSURL *largeArchiveURL = [self archiveWithFiles:emptyFiles - password:nil - name:[NSString stringWithFormat:@"Large Archive %ld", archiveNumber++]]; + name:[NSString stringWithFormat:@"Large Archive %ld", archiveNumber++] + zipOptions:nil]; return largeArchiveURL; } diff --git a/Tests/UnzipKitTests-Bridging-Header.h b/Tests/UnzipKitTests-Bridging-Header.h index 6ba7233..40fe7be 100644 --- a/Tests/UnzipKitTests-Bridging-Header.h +++ b/Tests/UnzipKitTests-Bridging-Header.h @@ -5,4 +5,5 @@ #import "UZKArchive.h" #import "zip.h" -#import "UZKArchiveTestCase.h" \ No newline at end of file +#import "UZKArchiveTestCase.h" +#import "UZKArchive+Deprecated.h" diff --git a/Tests/WriteBufferedDataTests.m b/Tests/WriteBufferedDataTests.m index bb541b0..ceda811 100644 --- a/Tests/WriteBufferedDataTests.m +++ b/Tests/WriteBufferedDataTests.m @@ -8,7 +8,8 @@ #import "UZKArchiveTestCase.h" #import "zip.h" -#import "UnzipKit.h" +@import UnzipKit; + @interface WriteBufferedDataTests : UZKArchiveTestCase @end @@ -18,11 +19,68 @@ @implementation WriteBufferedDataTests - (void)testWriteInfoBuffer { - NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; - NSArray *testDates = @[[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"], - [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/21/2014 10:00 AM"], - [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/22/2014 11:54 PM"]]; - NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; + NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSArray *testDates = @[[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/21/2014 10:00 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/22/2014 11:54 PM"]]; + NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; + + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"WriteIntoBufferTest.zip"]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + unsigned int bufferSize = 1024; //Arbitrary + + [testFiles enumerateObjectsUsingBlock:^(NSString *testFile, NSUInteger idx, BOOL *stop) { + NSData *fileData = [NSData dataWithContentsOfURL:self.testFileURLs[testFile]]; + [testFileData addObject:fileData]; + + NSError *writeError = nil; + const void *bytes = fileData.bytes; + + ZipFileProperties *props = [[ZipFileProperties alloc] init:testFile]; + props.timestamp = testDates[idx]; + + BOOL result = [archive writeIntoBuffer:props + error:&writeError + block: + ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { + for (NSUInteger i = 0; i <= fileData.length; i += bufferSize) { + unsigned int size = (unsigned int)MIN(fileData.length - i, bufferSize); + BOOL writeSuccess = writeData(&bytes[i], size); + XCTAssertTrue(writeSuccess, @"Failed to write buffered data"); + } + + return YES; + }]; + + XCTAssertTrue(result, @"Error writing archive data"); + XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); + }]; + + __block NSError *readError = nil; + __block NSUInteger idx = 0; + + [archive performOnDataInArchive:^(UZKFileInfo *fileInfo, NSData *fileData, BOOL *stop) { + NSData *expectedData = testFileData[idx]; + unsigned long expectedCRC = crc32(0, expectedData.bytes, (unsigned int)expectedData.length); + + XCTAssertEqualObjects(fileInfo.filename, testFiles[idx], @"Incorrect filename in archive"); + XCTAssertEqualObjects(fileInfo.timestamp, testDates[idx], @"Incorrect timestamp in archive"); + XCTAssertEqual(fileInfo.CRC, expectedCRC, @"CRC of extracted data doesn't match what was written"); + XCTAssertEqualObjects(fileData, expectedData, @"Data extracted doesn't match what was written"); + + idx++; + } error:&readError]; +} + +- (void)testWriteInfoBuffer_deprecatedOverload +{ + NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSArray *testDates = @[[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/21/2014 10:00 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/22/2014 11:54 PM"]]; + NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"WriteIntoBufferTest.zip"]; @@ -37,6 +95,8 @@ - (void)testWriteInfoBuffer NSError *writeError = nil; const void *bytes = fileData.bytes; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL result = [archive writeIntoBuffer:testFile fileDate:testDates[idx] compressionMethod:UZKCompressionMethodDefault @@ -56,6 +116,7 @@ - (void)testWriteInfoBuffer XCTAssertTrue(result, @"Error writing archive data"); XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); }]; +#pragma clang diagnostic pop __block NSError *readError = nil; __block NSUInteger idx = 0; @@ -83,6 +144,38 @@ - (void)testWriteInfoBuffer_Failure NSError *writeError = nil; + ZipFileProperties *props = [[ZipFileProperties alloc] init:@"Test File A.txt"]; + props.timestamp = [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"]; + + BOOL result = [archive writeIntoBuffer:props + error:&writeError + block: + ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { + NSError *bufferError = [NSError errorWithDomain:@"UnzipKitUnitTest" + code:errorCode + userInfo:@{}]; + *actionError = bufferError; + + return NO; + }]; + + XCTAssertFalse(result, @"Success returned during failure writing archive data"); + XCTAssertNotNil(writeError, @"No error after failure writing to archive"); + XCTAssertEqual(writeError.code, errorCode, @"Wrong error code returned"); +} + +- (void)testWriteInfoBuffer_Failure_deprecatedOverload +{ + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"WriteIntoBufferTest_Failure.zip"]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSInteger errorCode = 718; + + NSError *writeError = nil; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL result = [archive writeIntoBuffer:@"Test File A.txt" fileDate:[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"] compressionMethod:UZKCompressionMethodDefault @@ -97,7 +190,8 @@ - (void)testWriteInfoBuffer_Failure return NO; }]; - +#pragma clang diagnostic pop + XCTAssertFalse(result, @"Success returned during failure writing archive data"); XCTAssertNotNil(writeError, @"No error after failure writing to archive"); XCTAssertEqual(writeError.code, errorCode, @"Wrong error code returned"); @@ -120,6 +214,49 @@ - (void)testWriteInfoBuffer_PasswordGiven const void *bytes = fileData.bytes; + ZipFileProperties *props = [[ZipFileProperties alloc] init:testFile]; + props.crc = 841856539; + + BOOL result = [archive writeIntoBuffer:props + error:&writeError + block: + ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { + for (NSUInteger i = 0; i <= fileData.length; i += bufferSize) { + unsigned int size = (unsigned int)MIN(fileData.length - i, bufferSize); + BOOL writeSuccess = writeData(&bytes[i], size); + XCTAssertTrue(writeSuccess, @"Failed to write buffered data"); + } + + return YES; + }]; + + XCTAssertTrue(result, @"Error writing archive data"); + XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); + + BOOL extractSuccess = [self extractArchive:testArchiveURL + password:password]; + + XCTAssertTrue(extractSuccess, @"Failed to extract archive (encryption is incorrect)"); +} + +- (void)testWriteInfoBuffer_PasswordGiven_deprecatedOverload +{ + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"testWriteInfoBuffer_PasswordGiven.zip"]; + + NSString *password = @"a password"; + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL password:password error:nil]; + + NSError *writeError = nil; + + unsigned int bufferSize = 1024; //Arbitrary + + NSString *testFile = @"Test File A.txt"; + NSData *fileData = [NSData dataWithContentsOfURL:self.testFileURLs[testFile]]; + + const void *bytes = fileData.bytes; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL result = [archive writeIntoBuffer:testFile fileDate:nil compressionMethod:UZKCompressionMethodDefault @@ -136,7 +273,8 @@ - (void)testWriteInfoBuffer_PasswordGiven return YES; }]; - +#pragma clang diagnostic pop + XCTAssertTrue(result, @"Error writing archive data"); XCTAssertNil(writeError, @"Error writing to file %@: %@", testFile, writeError); @@ -155,6 +293,28 @@ - (void)testWriteInfoBuffer_PasswordGiven_NoCRC NSError *writeError = nil; + ZipFileProperties *props = [[ZipFileProperties alloc] init:@"Test File A.txt"]; + props.timestamp = [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"]; + + XCTAssertThrows([archive writeIntoBuffer:props + error:&writeError + block: + ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { + return YES; + }], + @"No assertion failed when streaming an encypted file with no CRC given"); +} + +- (void)testWriteInfoBuffer_PasswordGiven_NoCRC_deprecatedOverload +{ + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"testWriteInfoBuffer_PasswordGiven_NoCRC.zip"]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL password:@"a password" error:nil]; + + NSError *writeError = nil; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" XCTAssertThrows([archive writeIntoBuffer:@"Test File A.txt" fileDate:[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"] compressionMethod:UZKCompressionMethodDefault @@ -165,6 +325,7 @@ - (void)testWriteInfoBuffer_PasswordGiven_NoCRC return YES; }], @"No assertion failed when streaming an encypted file with no CRC given"); +#pragma clang diagnostic pop } - (void)testWriteInfoBuffer_PasswordGiven_MismatchedCRC @@ -182,6 +343,46 @@ - (void)testWriteInfoBuffer_PasswordGiven_MismatchedCRC const void *bytes = fileData.bytes; + ZipFileProperties *props = [[ZipFileProperties alloc] init:@"Test File A.txt"]; + props.crc = 3; + + BOOL result = [archive writeIntoBuffer:props + error:&writeError + block: + ^BOOL(BOOL(^writeData)(const void *bytes, unsigned int length), NSError**(actionError)) { + for (NSUInteger i = 0; i <= fileData.length; i += bufferSize) { + unsigned int size = (unsigned int)MIN(fileData.length - i, bufferSize); + BOOL writeSuccess = writeData(&bytes[i], size); + XCTAssertTrue(writeSuccess, @"Failed to write buffered data"); + } + + return YES; + }]; + + XCTAssertFalse(result, @"No error writing archive data"); + XCTAssertNotNil(writeError, @"No error writing to file"); + XCTAssertEqual(writeError.code, UZKErrorCodePreCRCMismatch, @"Wrong error code returned for CRC mismatch"); + XCTAssertTrue([writeError.localizedRecoverySuggestion containsString:@"0000000003"], @"Bad CRC not included in message"); + XCTAssertTrue([writeError.localizedRecoverySuggestion containsString:@"0841856539"], @"Good CRC not included in message"); +} + +- (void)testWriteInfoBuffer_PasswordGiven_MismatchedCRC_deprecatedOverload +{ + NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"testWriteInfoBuffer_PasswordGiven_MismatchedCRC.zip"]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL password:@"a password" error:nil]; + + NSError *writeError = nil; + + unsigned int bufferSize = 1024; //Arbitrary + + NSString *testFile = @"Test File A.txt"; + NSData *fileData = [NSData dataWithContentsOfURL:self.testFileURLs[testFile]]; + + const void *bytes = fileData.bytes; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL result = [archive writeIntoBuffer:testFile fileDate:nil compressionMethod:UZKCompressionMethodDefault @@ -198,7 +399,8 @@ - (void)testWriteInfoBuffer_PasswordGiven_MismatchedCRC return YES; }]; - +#pragma clang diagnostic pop + XCTAssertFalse(result, @"No error writing archive data"); XCTAssertNotNil(writeError, @"No error writing to file"); XCTAssertEqual(writeError.code, UZKErrorCodePreCRCMismatch, @"Wrong error code returned for CRC mismatch"); diff --git a/Tests/WriteDataTests.swift b/Tests/WriteDataTests.swift index c509ffa..84e0377 100644 --- a/Tests/WriteDataTests.swift +++ b/Tests/WriteDataTests.swift @@ -17,7 +17,7 @@ import UnzipKit class WriteDataTests: UZKArchiveTestCase { func testWriteData() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -32,8 +32,49 @@ class WriteDataTests: UZKArchiveTestCase { testFileData.append(fileData!) do { - try archive.write(fileData!, filePath: testFilePath, fileDate: testDates[index], - compressionMethod: .default, password: nil) + let props = ZipFileProperties(testFilePath) + props.timestamp = testDates[index] + try archive.write(fileData!, props: props) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePath): \(error)") + } + } + + var index = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + let expectedData = testFileData[index] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.filename, testFilePaths[index], "Incorrect filename in archive") + XCTAssertEqual(fileInfo.timestamp, testDates[index]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + index += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + } + + func testWriteData_deprecatedOverload() { + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) + let testDates = [ + UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/22/2014 11:54 PM")] + var testFileData = [Data]() + + let testArchiveURL = tempDirectory.appendingPathComponent("WriteDataTest.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + for (index, testFilePath) in testFilePaths.enumerated() { + let fileData = try? Data(contentsOf: testFileURLs[testFilePath] as! URL) + testFileData.append(fileData!) + + do { + try archive.deprecatedWrite(fileData!, filePath: testFilePath, fileDate: testDates[index], + compressionMethod: .default, password: nil) } catch let error as NSError { XCTFail("Error writing to file \(testFilePath): \(error)") } @@ -57,7 +98,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Unicode() { - let testFilePaths = [String](nonZipUnicodeFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -72,8 +113,49 @@ class WriteDataTests: UZKArchiveTestCase { testFileData.append(fileData!) do { - try archive.write(fileData!, filePath: testFilePath, fileDate: testDates[index], - compressionMethod: .default, password: nil) + let props = ZipFileProperties(testFilePath) + props.timestamp = testDates[index] + try archive.write(fileData!, props: props) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePath): \(error)") + } + } + + var index = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + let expectedData = testFileData[index] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.filename, testFilePaths[index], "Incorrect filename in archive") + XCTAssertEqual(fileInfo.timestamp, testDates[index]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + index += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + } + + func testWriteData_Unicode_deprecatedOverload() { + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) + let testDates = [ + UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/22/2014 11:54 PM")] + var testFileData = [Data]() + + let testArchiveURL = tempDirectory.appendingPathComponent("UnicodeWriteDataTest.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + for (index, testFilePath) in testFilePaths.enumerated() { + let fileData = try? Data(contentsOf: unicodeFileURLs[testFilePath] as! URL) + testFileData.append(fileData!) + + do { + try archive.deprecatedWrite(fileData!, filePath: testFilePath, fileDate: testDates[index], + compressionMethod: .default, password: nil) } catch let error as NSError { XCTFail("Error writing to file \(testFilePath): \(error)") } @@ -97,7 +179,83 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Overwrite() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) + let testDates = [ + UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/22/2014 11:54 PM")] + var testFileData = [Data]() + + let testArchiveURL = tempDirectory.appendingPathComponent("RewriteDataTest.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + for (index, testFilePath) in testFilePaths.enumerated() { + let fileData = try? Data(contentsOf: testFileURLs[testFilePath] as! URL) + testFileData.append(fileData!) + + do { + let props = ZipFileProperties(testFilePath) + props.timestamp = testDates[index] + try archive.write(fileData!, props: props) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePath): \(error)") + } + } + + var index = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + let expectedData = testFileData[index] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.filename, testFilePaths[index], "Incorrect filename in archive") + XCTAssertEqual(fileInfo.timestamp, testDates[index]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + index += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + + // Now write the files' contents to the zip in reverse + NSLog("Testing a second write, by reversing the contents and timestamps of the files from the first run") + + for i in 0.. Void in + XCTAssertEqual(fileInfo.filename, testFilePaths[forwardIndex], "Incorrect filename in archive"); + + let reverseIndex = testFilePaths.count - 1 - forwardIndex + + let expectedData = testFileData[reverseIndex] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.timestamp, testDates[reverseIndex]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + forwardIndex += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + } + + func testWriteData_Overwrite_deprecatedOverload() { + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -112,8 +270,8 @@ class WriteDataTests: UZKArchiveTestCase { testFileData.append(fileData!) do { - try archive.write(fileData!, filePath: testFilePath, fileDate: testDates[index], - compressionMethod: .default, password: nil) + try archive.deprecatedWrite(fileData!, filePath: testFilePath, fileDate: testDates[index], + compressionMethod: .default, password: nil) } catch let error as NSError { XCTFail("Error writing to file \(testFilePath): \(error)") } @@ -142,8 +300,8 @@ class WriteDataTests: UZKArchiveTestCase { let x = testFilePaths.count - 1 - i do { - try archive.write(testFileData[x], filePath: testFilePaths[i], - fileDate: testDates[x], compressionMethod: .default, password: nil) + try archive.deprecatedWrite(testFileData[x], filePath: testFilePaths[i], + fileDate: testDates[x], compressionMethod: .default, password: nil) } catch let error as NSError { XCTFail("Error writing to file \(testFilePaths[x]) with data of " + "file \(testFilePaths[i]): \(error)") @@ -171,7 +329,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Overwrite_Unicode() { - let testFilePaths = [String](nonZipUnicodeFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -186,8 +344,9 @@ class WriteDataTests: UZKArchiveTestCase { testFileData.append(fileData!) do { - try archive.write(fileData!, filePath: testFilePath, fileDate: testDates[index], - compressionMethod: .default, password: nil) + let props = ZipFileProperties(testFilePath) + props.timestamp = testDates[index] + try archive.write(fileData!, props: props) } catch let error as NSError { XCTFail("Error writing to file \(testFilePath): \(error)") } @@ -216,8 +375,83 @@ class WriteDataTests: UZKArchiveTestCase { let x = testFilePaths.count - 1 - i do { - try archive.write(testFileData[x], filePath: testFilePaths[i], - fileDate: testDates[x], compressionMethod: .default, password: nil) + let props = ZipFileProperties(testFilePaths[i]) + props.timestamp = testDates[x] + try archive.write(testFileData[x], props: props) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePaths[x]) with data of " + + "file \(testFilePaths[i]): \(error)") + } + } + + var forwardIndex = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + XCTAssertEqual(fileInfo.filename, testFilePaths[forwardIndex], "Incorrect filename in archive"); + + let reverseIndex = testFilePaths.count - 1 - forwardIndex + + let expectedData = testFileData[reverseIndex] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.timestamp, testDates[reverseIndex]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + forwardIndex += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + } + + func testWriteData_Overwrite_Unicode_deprecatedOverload() { + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) + let testDates = [ + UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/22/2014 11:54 PM")] + var testFileData = [Data]() + + let testArchiveURL = tempDirectory.appendingPathComponent("RewriteDataTest.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + for (index, testFilePath) in testFilePaths.enumerated() { + let fileData = try? Data(contentsOf: unicodeFileURLs[testFilePath] as! URL) + testFileData.append(fileData!) + + do { + try archive.deprecatedWrite(fileData!, filePath: testFilePath, fileDate: testDates[index], + compressionMethod: .default, password: nil) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePath): \(error)") + } + } + + var index = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + let expectedData = testFileData[index] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.filename, testFilePaths[index], "Incorrect filename in archive") + XCTAssertEqual(fileInfo.timestamp, testDates[index]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + index += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + + // Now write the files' contents to the zip in reverse + NSLog("Testing a second write, by reversing the contents and timestamps of the files from the first run") + + for i in 0..).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) + let testDates = [ + UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), + UZKArchiveTestCase.dateFormatter().date(from: "12/22/2014 11:54 PM")] + var testFileData = [Data]() + + let testArchiveURL = tempDirectory.appendingPathComponent("RewriteDataTest.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + for (index, testFilePath) in testFilePaths.enumerated() { + let fileData = try? Data(contentsOf: testFileURLs[testFilePath] as! URL) + testFileData.append(fileData!) + + do { + let props = ZipFileProperties(testFilePath) + props.overwriteIfInArchive = false + props.timestamp = testDates[index] + try archive.write(fileData!, props: props) + } catch let error as NSError { + XCTFail("Error writing to file \(testFilePath): \(error)") + } + } + + var index = 0 + + try! archive.performOnData(inArchive: { (fileInfo, fileData, stop) -> Void in + let expectedData = testFileData[index] + let expectedCRC = crc32(0, (expectedData as NSData).bytes.bindMemory(to: Bytef.self, capacity: expectedData.count), uInt(expectedData.count)) + + XCTAssertEqual(fileInfo.filename, testFilePaths[index], "Incorrect filename in archive") + XCTAssertEqual(fileInfo.timestamp, testDates[index]!, "Incorrect timestamp in archive") + XCTAssertEqual(fileInfo.crc, expectedCRC, "CRC of extracted data doesn't match what was written") + XCTAssertEqual(fileData, expectedData, "Data extracted doesn't match what was written") + + index += 1; + }) + + XCTAssert(index > 0, "No data iterated through") + + // Now write the files' contents to the zip in reverse + + for i in 0.. 0 { + XCTAssertEqual(lastFileSize, fileSize.uint64Value, "File changed size between writes") + } + + lastFileSize = fileSize.uint64Value + } + } + + func testWriteData_MultipleWrites_deprecatedOverload() { + let testArchiveURL = tempDirectory.appendingPathComponent("MultipleDataWriteTest.zip") + let testFilename = nonZipTestFilePaths.first! + let testFileURL = testFileURLs[testFilename] as! URL + let testFileData = try! Data(contentsOf: testFileURL) + + let archive = try! UZKArchive(url: testArchiveURL) + + var lastFileSize: UInt64 = 0 + + for _ in 0..<100 { + do { + try archive.deprecatedWrite(testFileData, filePath: testFilename, fileDate: nil, + compressionMethod: .default, password: nil) + } catch let error as NSError { + XCTFail("Error writing to file \(testFileURL): \(error)") + } + + let fm = FileManager.default + let fileAttributes = try! fm.attributesOfItem(atPath: testArchiveURL.path) let fileSize = fileAttributes[FileAttributeKey.size] as! NSNumber if lastFileSize > 0 { @@ -353,15 +679,38 @@ class WriteDataTests: UZKArchiveTestCase { func testWriteData_DefaultDate() { let testArchiveURL = tempDirectory.appendingPathComponent("DefaultDateWriteTest.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! + let testFileURL = testFileURLs[testFilename] as! URL + let testFileData = try! Data(contentsOf: testFileURL) + + let archive = try! UZKArchive(url: testArchiveURL) + + do { + try archive.write(testFileData, filePath: testFilename) + } catch let error as NSError { + XCTFail("Error writing to file \(testFileURL): \(error)") + } + + let fileList = try! archive.listFileInfo() + let writtenFileInfo = fileList.first! + + let expectedDate = Date().timeIntervalSinceReferenceDate + let actualDate = writtenFileInfo.timestamp.timeIntervalSinceReferenceDate + + XCTAssertEqual(actualDate, expectedDate, accuracy: 30, "Incorrect default date value written to file") + } + + func testWriteData_DefaultDate_deprecatedOverload() { + let testArchiveURL = tempDirectory.appendingPathComponent("DefaultDateWriteTest.zip") + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) let archive = try! UZKArchive(url: testArchiveURL) do { - try archive.write(testFileData, filePath: testFilename, fileDate: nil, - compressionMethod: .default, password: nil) + try archive.deprecatedWrite(testFileData, filePath: testFilename, fileDate: nil, + compressionMethod: .default, password: nil) } catch let error as NSError { XCTFail("Error writing to file \(testFileURL): \(error)") } @@ -377,13 +726,13 @@ class WriteDataTests: UZKArchiveTestCase { #if os(OSX) func testWriteData_PasswordProtected() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) var testFileData = [Data]() let testArchiveURL = tempDirectory.appendingPathComponent("SwiftWriteDataTest.zip") let password = "111111" - let writeArchive = try! UZKArchive(path: testArchiveURL.path, password: password) + let writeArchive = try! UZKArchive(url: testArchiveURL, password: password) for testFilePath in testFilePaths { let fileData = try? Data(contentsOf: testFileURLs[testFilePath] as! URL) @@ -398,7 +747,7 @@ class WriteDataTests: UZKArchiveTestCase { // Read with UnzipKit - let readArchive = try! UZKArchive(path: testArchiveURL.path, password: password) + let readArchive = try! UZKArchive(url: testArchiveURL, password: password) XCTAssertTrue(readArchive.isPasswordProtected(), "Archive is not marked as password-protected") var index = 0 @@ -449,9 +798,52 @@ class WriteDataTests: UZKArchiveTestCase { let newTextData = newText.data(using: String.Encoding.utf8) var writeSuccessful = true do { - try archive.write(newTextData!, filePath: textFileName, fileDate: nil, - compressionMethod: UZKCompressionMethod.default, password: nil, - overwrite: true) + try archive.write(newTextData!, filePath: textFileName) + } catch let error { + NSLog("Error writing data to archive on external volume: \(error)") + writeSuccessful = false + } + + XCTAssertTrue(writeSuccessful, "Failed to update archive on external volume") + + let archivedFileData = try! archive.extractData(fromFile: textFileName) + XCTAssertNotNil(archivedFileData, "No data extracted from file in archive on external volume") + + let archivedText = NSString(data: archivedFileData, encoding: String.Encoding.utf8.rawValue)! + XCTAssertEqual(archivedText as String, newText, "Incorrect text extracted from archive on external volume") + } + + func testWriteData_ExternalVolume_deprecatedOverload() { + // Create a simple zip file + let tempDirURL = URL(fileURLWithPath: self.randomDirectoryName()) + let textFileName = "testWriteData_ExternalVolume.txt" + let textFileURL = tempDirURL.appendingPathComponent(textFileName) + try! FileManager.default.createDirectory(at: tempDirURL, withIntermediateDirectories: true, attributes: [:]) + try! "This is the original text".write(to: textFileURL, atomically: false, encoding: String.Encoding.utf8) + let tempZipFileURL = self.archive(withFiles: [textFileURL]) + NSLog("Original ZIP file: \(String(describing: tempZipFileURL?.path))") + + // Write that zip file to contents of a DMG and mount it + let dmgSourceFolderURL = tempDirURL.appendingPathComponent("DMGSource") + try! FileManager.default.createDirectory(at: dmgSourceFolderURL, withIntermediateDirectories: true, attributes: [:]) + try! FileManager.default.copyItem(at: tempZipFileURL!, to: dmgSourceFolderURL.appendingPathComponent(tempZipFileURL!.lastPathComponent)) + let dmgURL = tempDirURL.appendingPathComponent("testWriteData_ExternalVolume.dmg") + let mountPoint = createAndMountDMG(path: dmgURL, source: dmgSourceFolderURL, fileSystem: .HFS)! + NSLog("Disk image: \(dmgURL.path)") + defer { + unmountDMG(URL: mountPoint) + } + + // Update a file from the archive with overwrite=YES + let externalVolumeZipURL = mountPoint.appendingPathComponent(tempZipFileURL!.lastPathComponent) + let archive = try! UZKArchive(url: externalVolumeZipURL) + let newText = "This is the new text" + let newTextData = newText.data(using: String.Encoding.utf8) + var writeSuccessful = true + do { + try archive.deprecatedWrite(newTextData!, filePath: textFileName, fileDate: nil, + compressionMethod: UZKCompressionMethod.default, password: nil, + overwrite: true) } catch let error { NSLog("Error writing data to archive on external volume: \(error)") writeSuccessful = false diff --git a/Tests/ZipFileDetectionTests.m b/Tests/ZipFileDetectionTests.m index 34d5384..c35b1af 100644 --- a/Tests/ZipFileDetectionTests.m +++ b/Tests/ZipFileDetectionTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +@import UnzipKit; @interface ZipFileDetectionTests : UZKArchiveTestCase @end diff --git a/UnzipKit.podspec b/UnzipKit.podspec index 7b54d19..0a33a0c 100644 --- a/UnzipKit.podspec +++ b/UnzipKit.podspec @@ -7,25 +7,38 @@ Pod::Spec.new do |s| s.author = { "Dov Frankel" => "dov@abbey-code.com" } s.social_media_url = "https://twitter.com/dovfrankel" s.source = { :git => "https://github.com/abbeycode/UnzipKit.git", :tag => "#{s.version}" } - s.ios.deployment_target = "9.0" - s.osx.deployment_target = "10.9" + s.ios.deployment_target = "12.0" + s.osx.deployment_target = "10.14" + s.swift_version = "5.0" + s.library = "z" s.requires_arc = 'Source/**/*' s.public_header_files = "Source/UnzipKit.h", "Source/UZKArchive.h", "Source/UZKFileInfo.h" s.private_header_files = "Source/UZKFileInfo_Private.h" - s.source_files = "Source/**/*.{h,m}" + s.source_files = "Source/**/*.{h,m,swift}" s.exclude_files = 'Resources/**/Info.plist' + s.preserve_paths = 'Scripts/generate-swift-import-header.sh' s.resource_bundles = { 'UnzipKitResources' => ['Resources/**/*'] } + s.script_phases = { :name => "Generate UnzipKit Swift Header", + :script => "\"${PODS_TARGET_SRCROOT}\"/Scripts/generate-swift-import-header.sh", + :execution_position => :before_compile } s.test_spec 'Tests' do |test_spec| - test_spec.source_files = 'Tests/*.{h,m}' + test_spec.source_files = 'Tests/*.{h,m,swift}' test_spec.exclude_files = 'Tests/ExtractFilesTests.m' + test_spec.preserve_paths = 'Tests/UnzipKitTests-Bridging-Header.h' test_spec.resources = ['Tests/Test Data'] - test_spec.pod_target_xcconfig = { "OTHER_CFLAGS" => "$(inherited) -Wno-unguarded-availability" } + test_spec.pod_target_xcconfig = { + "OTHER_CFLAGS" => "$(inherited) -Wno-unguarded-availability", + "SWIFT_OBJC_BRIDGING_HEADER" => "$(PODS_TARGET_SRCROOT)/Tests/UnzipKitTests-Bridging-Header.h" + } + test_spec.scheme = { + # Disable logging. Comment this line if you need diagnostic info + :environment_variables => { "OS_ACTIVITY_MODE" => "disable" } + } end - s.library = "z" s.subspec "minizip-lib" do |ss| ss.private_header_files = "Lib/MiniZip/*.h" diff --git a/UnzipKit.xcodeproj/project.pbxproj b/UnzipKit.xcodeproj/project.pbxproj index 2986ac9..29be66a 100644 --- a/UnzipKit.xcodeproj/project.pbxproj +++ b/UnzipKit.xcodeproj/project.pbxproj @@ -17,9 +17,13 @@ 7A0029221F93DBC900618503 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA65C81A40C44300685B6D /* unzip.h */; }; 7A0029231F93DBC900618503 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA65CA1A40C44300685B6D /* zip.h */; }; 7A0029241F93DBF000618503 /* libminizip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A0029171F93DB5800618503 /* libminizip.a */; }; + 7A27405422EAA3A500E23E75 /* FileInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */; }; 7A5652241F90E01C006B782E /* CheckDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A5652231F90E01C006B782E /* CheckDataTests.m */; }; 7A5A97011F89808900BCA061 /* ProgressReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */; }; 7AA77FC822C16CF600121052 /* PermissionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA77FC722C16CF600121052 /* PermissionsTests.swift */; }; + 7AA9F194230199990076AD43 /* UZKArchive+Deprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA9F192230199990076AD43 /* UZKArchive+Deprecated.h */; }; + 7AA9F195230199990076AD43 /* UZKArchive+Deprecated.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AA9F193230199990076AD43 /* UZKArchive+Deprecated.m */; }; + 7AB0E6AA22F3179F00AAE12F /* ZipFileProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0E6A922F3179F00AAE12F /* ZipFileProperties.swift */; }; 961A9BB51B306881007C4C6B /* WriteDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961A9BB41B306881007C4C6B /* WriteDataTests.swift */; }; 962F9DA61D5D286B00205BEC /* UnzipKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = 962F9DA41D5D286B00205BEC /* UnzipKit.strings */; }; 962F9DA81D5D288B00205BEC /* UnzipKitResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 962F9D9E1D5D281E00205BEC /* UnzipKitResources.bundle */; }; @@ -77,9 +81,13 @@ /* Begin PBXFileReference section */ 7A0029171F93DB5800618503 /* libminizip.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminizip.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileInfoTests.swift; sourceTree = ""; }; 7A5652231F90E01C006B782E /* CheckDataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CheckDataTests.m; sourceTree = ""; }; 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgressReportingTests.m; sourceTree = ""; }; 7AA77FC722C16CF600121052 /* PermissionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsTests.swift; sourceTree = ""; }; + 7AA9F192230199990076AD43 /* UZKArchive+Deprecated.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UZKArchive+Deprecated.h"; sourceTree = ""; }; + 7AA9F193230199990076AD43 /* UZKArchive+Deprecated.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UZKArchive+Deprecated.m"; sourceTree = ""; }; + 7AB0E6A922F3179F00AAE12F /* ZipFileProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipFileProperties.swift; sourceTree = ""; }; 961A9BB31B306880007C4C6B /* UnzipKitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "UnzipKitTests-Bridging-Header.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 961A9BB41B306881007C4C6B /* WriteDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WriteDataTests.swift; sourceTree = ""; }; 961A9BB61B306902007C4C6B /* UZKArchiveTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UZKArchiveTestCase.h; sourceTree = ""; }; @@ -91,6 +99,9 @@ 965CF0081D241A8F00C80A88 /* NSURL+UnzipKitExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+UnzipKitExtensions.h"; sourceTree = ""; }; 965CF0091D241A8F00C80A88 /* NSURL+UnzipKitExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+UnzipKitExtensions.m"; sourceTree = ""; }; 9677858D1F1405DB00A8D6B2 /* UnzipKitMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnzipKitMacros.h; sourceTree = ""; }; + 96855F4425FDD42000A4848F /* Complete.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Complete.xctestplan; sourceTree = ""; }; + 96855F4D25FDD4C500A4848F /* Fast.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Fast.xctestplan; sourceTree = ""; }; + 96855F5325FDD61500A4848F /* Rigorous.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Rigorous.xctestplan; sourceTree = ""; }; 96889EED1E4A09EC0031A322 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/UnzipKit.strings; sourceTree = ""; }; 968C40BF1B585FDE004C128E /* ModesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ModesTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 968C40C11B586132004C128E /* ZipFileDetectionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ZipFileDetectionTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -189,6 +200,16 @@ path = Extensions; sourceTree = ""; }; + 96855F4E25FDD4DD00A4848F /* Test Plans */ = { + isa = PBXGroup; + children = ( + 96855F4D25FDD4C500A4848F /* Fast.xctestplan */, + 96855F4425FDD42000A4848F /* Complete.xctestplan */, + 96855F5325FDD61500A4848F /* Rigorous.xctestplan */, + ); + path = "Test Plans"; + sourceTree = ""; + }; 96EA65941A40AEAE00685B6D = { isa = PBXGroup; children = ( @@ -223,6 +244,7 @@ 96EA65FF1A40E31900685B6D /* UZKFileInfo.h */, 963603521BFB7F6500BF0C4F /* UZKFileInfo_Private.h */, 96EA66001A40E31900685B6D /* UZKFileInfo.m */, + 7AB0E6A922F3179F00AAE12F /* ZipFileProperties.swift */, 96EA65A11A40AEAE00685B6D /* Supporting Files */, ); name = UnzipKit; @@ -240,9 +262,12 @@ 96EA65AD1A40AEAE00685B6D /* UnzipKitTests */ = { isa = PBXGroup; children = ( + 96855F4E25FDD4DD00A4848F /* Test Plans */, 96EA65BF1A40BF1A00685B6D /* Test Data */, 961A9BB61B306902007C4C6B /* UZKArchiveTestCase.h */, 96FCC8401B306CDD00726AC7 /* UZKArchiveTestCase.m */, + 7AA9F192230199990076AD43 /* UZKArchive+Deprecated.h */, + 7AA9F193230199990076AD43 /* UZKArchive+Deprecated.m */, 963386B71EE89A49006B16BF /* UtilityMethods.swift */, 7A5652231F90E01C006B782E /* CheckDataTests.m */, 968C40DB1B586401004C128E /* CommentsTests.m */, @@ -252,6 +277,7 @@ 968C40C91B586227004C128E /* ExtractDataTests.m */, 9630C0371C6D27A4000693EE /* ExtractDataTests_Swift.swift */, 968C40C71B5861F4004C128E /* ExtractFilesTests.m */, + 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */, 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */, 968C40D91B5863D9004C128E /* FileDescriptorUsageTests.m */, 968C40C31B58619C004C128E /* ListFilenamesTests.m */, @@ -335,6 +361,7 @@ buildActionMask = 2147483647; files = ( 96EA65BC1A40B2EC00685B6D /* UZKArchive.h in Headers */, + 7AA9F194230199990076AD43 /* UZKArchive+Deprecated.h in Headers */, 96EA66011A40E31900685B6D /* UZKFileInfo.h in Headers */, 96EA65A41A40AEAE00685B6D /* UnzipKit.h in Headers */, 963603531BFB815600BF0C4F /* UZKFileInfo_Private.h in Headers */, @@ -384,6 +411,7 @@ isa = PBXNativeTarget; buildConfigurationList = 96EA65B41A40AEAE00685B6D /* Build configuration list for PBXNativeTarget "UnzipKit" */; buildPhases = ( + 7A900754230AD74C004A95E5 /* ShellScript */, 96EA65991A40AEAE00685B6D /* Sources */, 96EA659A1A40AEAE00685B6D /* Frameworks */, 96EA659B1A40AEAE00685B6D /* Headers */, @@ -425,7 +453,7 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = "Abbey Code"; TargetAttributes = { 7A0029161F93DB5800618503 = { @@ -437,17 +465,17 @@ }; 96EA659D1A40AEAE00685B6D = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 0830; + LastSwiftMigration = 1030; }; 96EA65A81A40AEAE00685B6D = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 0900; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = 96EA65981A40AEAE00685B6D /* Build configuration list for PBXProject "UnzipKit" */; compatibilityVersion = "Xcode 8.0"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -493,6 +521,26 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 7A900754230AD74C004A95E5 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SOURCE_ROOT}\"/Scripts/generate-swift-import-header.sh\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 7A0029131F93DB5800618503 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -516,6 +564,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7AB0E6AA22F3179F00AAE12F /* ZipFileProperties.swift in Sources */, + 7AA9F195230199990076AD43 /* UZKArchive+Deprecated.m in Sources */, 96EA65BD1A40B2EC00685B6D /* UZKArchive.m in Sources */, 96EA66021A40E31900685B6D /* UZKFileInfo.m in Sources */, 965CF00C1D241A8F00C80A88 /* NSURL+UnzipKitExtensions.m in Sources */, @@ -545,6 +595,7 @@ 968C40E01B586490004C128E /* PropertyTests.m in Sources */, 968C40DC1B586401004C128E /* CommentsTests.m in Sources */, 968C40D21B586310004C128E /* PasswordProtectionTests.m in Sources */, + 7A27405422EAA3A500E23E75 /* FileInfoTests.swift in Sources */, 963386B91EE89A51006B16BF /* UtilityMethods.swift in Sources */, 7AA77FC822C16CF600121052 /* PermissionsTests.swift in Sources */, 968C40C81B5861F4004C128E /* ExtractFilesTests.m in Sources */, @@ -675,6 +726,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -693,6 +745,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -706,8 +759,8 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", "$(inherited)", + "DEBUG=1", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -716,12 +769,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Weverything", "-Wno-auto-import", @@ -735,6 +788,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -753,6 +807,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -771,12 +826,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Weverything", "-Wno-auto-import", @@ -802,6 +857,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -821,6 +877,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -829,14 +886,18 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "CODE_IMPORTED_AS_LIB=1", + ); INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DT_TOOLCHAIN_DIR)/usr/lib/swift/iphonesimulator"; + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnzipKitTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; WARNING_CFLAGS = ( "$(inherited)", "-Wno-gnu-statement-expression", @@ -846,6 +907,7 @@ "-Wno-pointer-arith", "-Wno-cast-qual", "-Wno-undef", + "-Wno-objc-messaging-id", ); }; name = Debug; @@ -855,12 +917,14 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; + GCC_PREPROCESSOR_DEFINITIONS = "CODE_IMPORTED_AS_LIB=1"; INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(DT_TOOLCHAIN_DIR)/usr/lib/swift/iphonesimulator"; + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnzipKitTests-Bridging-Header.h"; - SWIFT_VERSION = 4.0; WARNING_CFLAGS = ( "$(inherited)", "-Wno-everything", diff --git a/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme b/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme index 7ead498..ce7125f 100644 --- a/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme +++ b/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme @@ -1,7 +1,7 @@ + LastUpgradeVersion = "1240" + version = "1.7"> @@ -41,6 +41,27 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + + + + @@ -53,17 +74,6 @@ - - - - - - - - "..", :testspecs => ['Tests'] diff --git a/UnzipKitDemo/UnzipKitDemo.xcodeproj/project.pbxproj b/UnzipKitDemo/UnzipKitDemo.xcodeproj/project.pbxproj index 08fa86f..598b422 100644 --- a/UnzipKitDemo/UnzipKitDemo.xcodeproj/project.pbxproj +++ b/UnzipKitDemo/UnzipKitDemo.xcodeproj/project.pbxproj @@ -14,12 +14,11 @@ 969E2FEC1AD573F100E19F7A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 969E2FEB1AD573F100E19F7A /* Images.xcassets */; }; 969E2FEF1AD573F100E19F7A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 969E2FED1AD573F100E19F7A /* LaunchScreen.xib */; }; 969E30091AD57BD000E19F7A /* Test Data in Resources */ = {isa = PBXBuildFile; fileRef = 969E30081AD57BD000E19F7A /* Test Data */; }; - D058D1B1EDCAA17D37F1EEC5 /* libPods-UnzipKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 836C8D3D2EBCC1B567DEBA7E /* libPods-UnzipKitDemo.a */; }; + E503D0FBEE04B7BC727B57B6 /* libPods-UnzipKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3795F70E736484A471E6F15 /* libPods-UnzipKitDemo.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 2F87D9A1D0DCA68C622CCE05 /* Pods-UnzipKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnzipKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo.debug.xcconfig"; sourceTree = ""; }; - 836C8D3D2EBCC1B567DEBA7E /* libPods-UnzipKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnzipKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 969381281D209CF5001B2ED8 /* libPods-UnzipKitDemo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-UnzipKitDemo.a"; path = "../../../Library/Developer/Xcode/DerivedData/UnzipKitDemo-hdmwkglyskhqhncppjlrcqmehjld/Build/Products/Debug-iphonesimulator/libPods-UnzipKitDemo.a"; sourceTree = ""; }; 9693812A1D209CFA001B2ED8 /* libUnzipKit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libUnzipKit.a; path = "../../../Library/Developer/Xcode/DerivedData/UnzipKitDemo-hdmwkglyskhqhncppjlrcqmehjld/Build/Products/Debug-iphonesimulator/UnzipKit/libUnzipKit.a"; sourceTree = ""; }; 969E2FDF1AD573F100E19F7A /* UnzipKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UnzipKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -32,6 +31,7 @@ 969E30041AD5798D00E19F7A /* UnzipKitDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnzipKitDemo-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; 969E30081AD57BD000E19F7A /* Test Data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Test Data"; path = "../../Tests/Test Data"; sourceTree = ""; }; D07031CAE5D79F16E6A159A8 /* Pods-UnzipKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnzipKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo.release.xcconfig"; sourceTree = ""; }; + D3795F70E736484A471E6F15 /* libPods-UnzipKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnzipKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -40,7 +40,7 @@ buildActionMask = 2147483647; files = ( 9693812B1D209CFA001B2ED8 /* libUnzipKit.a in Frameworks */, - D058D1B1EDCAA17D37F1EEC5 /* libPods-UnzipKitDemo.a in Frameworks */, + E503D0FBEE04B7BC727B57B6 /* libPods-UnzipKitDemo.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,7 +102,7 @@ children = ( 9693812A1D209CFA001B2ED8 /* libUnzipKit.a */, 969381281D209CF5001B2ED8 /* libPods-UnzipKitDemo.a */, - 836C8D3D2EBCC1B567DEBA7E /* libPods-UnzipKitDemo.a */, + D3795F70E736484A471E6F15 /* libPods-UnzipKitDemo.a */, ); name = Frameworks; sourceTree = ""; @@ -118,7 +118,7 @@ 969E2FDB1AD573F100E19F7A /* Sources */, 969E2FDC1AD573F100E19F7A /* Frameworks */, 969E2FDD1AD573F100E19F7A /* Resources */, - FFBBDB5F70B02F25F7B1C9FA /* [CP] Copy Pods Resources */, + E71BD50F86C41C113D4362A9 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -136,7 +136,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Abbey Code"; TargetAttributes = { 969E2FDE1AD573F100E19F7A = { @@ -147,7 +147,7 @@ }; buildConfigurationList = 969E2FDA1AD573F100E19F7A /* Build configuration list for PBXProject "UnzipKitDemo" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -196,13 +196,13 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - FFBBDB5F70B02F25F7B1C9FA /* [CP] Copy Pods Resources */ = { + E71BD50F86C41C113D4362A9 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/UnzipKit/UnzipKitResources.bundle", ); name = "[CP] Copy Pods Resources"; @@ -211,7 +211,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-UnzipKitDemo/Pods-UnzipKitDemo-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -252,6 +252,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -295,12 +296,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -309,6 +310,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -345,11 +347,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -368,7 +370,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "UnzipKitDemo-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -384,7 +385,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "UnzipKitDemo-Bridging-Header.h"; - SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/UnzipKitDemo/UnzipKitDemo.xcodeproj/xcshareddata/xcschemes/UnzipKitDemo.xcscheme b/UnzipKitDemo/UnzipKitDemo.xcodeproj/xcshareddata/xcschemes/UnzipKitDemo.xcscheme index c4cbc20..fa4e044 100644 --- a/UnzipKitDemo/UnzipKitDemo.xcodeproj/xcshareddata/xcschemes/UnzipKitDemo.xcscheme +++ b/UnzipKitDemo/UnzipKitDemo.xcodeproj/xcshareddata/xcschemes/UnzipKitDemo.xcscheme @@ -1,6 +1,6 @@ diff --git a/UnzipKitDemo/UnzipKitDemo/AppDelegate.swift b/UnzipKitDemo/UnzipKitDemo/AppDelegate.swift index 14d82be..90f463f 100644 --- a/UnzipKitDemo/UnzipKitDemo/AppDelegate.swift +++ b/UnzipKitDemo/UnzipKitDemo/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/beta-notes.md b/beta-notes.md index e908b45..9e18975 100644 --- a/beta-notes.md +++ b/beta-notes.md @@ -1 +1,7 @@ -Added support for archiving and restoring files' POSIX permissions (PRs #84, #86, #87 - Thanks, [@MartinLau7](https://github.com/MartinLau7)!) \ No newline at end of file +* Removed methods deprecated in v1.9 (Issue #90, PR #92) +* Fixed behavior of `-extractFilesTo:overwrite:error:`, so it shows the progress of each individual file as they extract (Issue #91, PR #94) +* Deprecated the initializers that take a file path instead of an `NSURL` (Issue #90, PR #95) + +In this release: +* Fixed some potential crashing bugs +* Fixed some newer Xcode/Swift warnings \ No newline at end of file