2015-10-23 14:15:31 -04:00
//
// SSZipArchive.m
// SSZipArchive
//
// Created by Sam Soffes on 7/21/10.
// Copyright (c) Sam Soffes 2010-2015. All rights reserved.
//
2017-09-09 23:05:11 +08:00
2015-10-23 14:15:31 -04:00
# import "SSZipArchive.h"
# include "unzip.h"
# include "zip.h"
2017-06-22 12:06:35 +08:00
# include "minishared.h"
2015-10-23 14:15:31 -04:00
# include <sys/stat.h>
2017-03-28 09:14:39 -06:00
NSString * const SSZipArchiveErrorDomain = @" SSZipArchiveErrorDomain " ;
2015-10-23 14:15:31 -04:00
# define CHUNK 16384
@interface SSZipArchive ( )
+ ( NSDate * ) _dateWithMSDOSFormat : ( UInt32 ) msdosDateTime ;
2017-09-14 00:31:27 +08:00
- ( instancetype ) init NS_DESIGNATED_INITIALIZER ;
2015-10-23 14:15:31 -04:00
@end
@implementation SSZipArchive
{
2015-11-24 10:31:51 -08:00
NSString * _path ;
2015-10-23 14:15:31 -04:00
zipFile _zip ;
}
2016-07-02 18:52:36 +02:00
# pragma mark - Password check
+ ( BOOL ) isFilePasswordProtectedAtPath: ( NSString * ) path {
// Begin opening
2017-09-14 00:31:27 +08:00
zipFile zip = unzOpen ( path . fileSystemRepresentation ) ;
2016-07-02 18:52:36 +02:00
if ( zip = = NULL ) {
return NO ;
}
int ret = unzGoToFirstFile ( zip ) ;
if ( ret = = UNZ_OK ) {
do {
ret = unzOpenCurrentFile ( zip ) ;
2017-02-24 13:32:28 +08:00
if ( ret ! = UNZ_OK ) {
2016-07-02 18:52:36 +02:00
return NO ;
}
2017-02-24 13:32:28 +08:00
unz_file_info fileInfo = { 0 } ;
2016-07-02 18:52:36 +02:00
ret = unzGetCurrentFileInfo ( zip , & fileInfo , NULL , 0 , NULL , 0 , NULL , 0 ) ;
2017-02-24 13:32:28 +08:00
if ( ret ! = UNZ_OK ) {
2016-07-02 18:52:36 +02:00
return NO ;
2017-02-24 13:32:28 +08:00
} else if ( ( fileInfo . flag & 1 ) = = 1 ) {
2016-07-02 18:52:36 +02:00
return YES ;
}
unzCloseCurrentFile ( zip ) ;
ret = unzGoToNextFile ( zip ) ;
2017-02-24 13:32:28 +08:00
} while ( ret = = UNZ_OK & & UNZ_OK ! = UNZ_END_OF_LIST_OF_FILE ) ;
2016-07-02 18:52:36 +02:00
}
return NO ;
}
2016-11-05 15:14:30 +01:00
+ ( BOOL ) isPasswordValidForArchiveAtPath: ( NSString * ) path password: ( NSString * ) pw error: ( NSError * * ) error {
if ( error ) {
* error = nil ;
}
2017-09-14 00:31:27 +08:00
zipFile zip = unzOpen ( path . fileSystemRepresentation ) ;
2016-11-05 15:14:30 +01:00
if ( zip = = NULL ) {
if ( error ) {
2017-03-15 10:57:46 +01:00
* error = [ NSError errorWithDomain : SSZipArchiveErrorDomain
code : SSZipArchiveErrorCodeFailedOpenZipFile
2016-11-05 15:14:30 +01:00
userInfo : @{ NSLocalizedDescriptionKey : @" failed to open zip file " } ] ;
}
return NO ;
}
int ret = unzGoToFirstFile ( zip ) ;
if ( ret = = UNZ_OK ) {
do {
2017-09-14 00:31:27 +08:00
if ( pw . length = = 0 ) {
2016-11-05 15:14:30 +01:00
ret = unzOpenCurrentFile ( zip ) ;
} else {
2017-09-16 18:01:56 +08:00
ret = unzOpenCurrentFilePassword ( zip , [ pw cStringUsingEncoding : NSUTF8StringEncoding ] ) ;
2016-11-05 15:14:30 +01:00
}
if ( ret ! = UNZ_OK ) {
if ( ret ! = UNZ_BADPASSWORD ) {
if ( error ) {
2017-03-15 10:57:46 +01:00
* error = [ NSError errorWithDomain : SSZipArchiveErrorDomain
2017-06-22 03:28:37 +08:00
code : SSZipArchiveErrorCodeFailedOpenFileInZip
2016-11-05 15:14:30 +01:00
userInfo : @{ NSLocalizedDescriptionKey : @" failed to open first file in zip file " } ] ;
}
}
return NO ;
}
unz_file_info fileInfo = { 0 } ;
ret = unzGetCurrentFileInfo ( zip , & fileInfo , NULL , 0 , NULL , 0 , NULL , 0 ) ;
if ( ret ! = UNZ_OK ) {
if ( error ) {
2017-03-15 10:57:46 +01:00
* error = [ NSError errorWithDomain : SSZipArchiveErrorDomain
code : SSZipArchiveErrorCodeFileInfoNotLoadable
2016-11-05 15:14:30 +01:00
userInfo : @{ NSLocalizedDescriptionKey : @" failed to retrieve info for file " } ] ;
}
return NO ;
2017-02-24 13:32:28 +08:00
} else if ( ( fileInfo . flag & 1 ) = = 1 ) {
2016-11-05 15:14:30 +01:00
unsigned char buffer [ 10 ] = { 0 } ;
int readBytes = unzReadCurrentFile ( zip , buffer , ( unsigned ) MIN ( 10UL , fileInfo . uncompressed_size ) ) ;
if ( readBytes < 0 ) {
// Let's assume the invalid password caused this error
if ( readBytes ! = Z_DATA_ERROR ) {
if ( error ) {
2017-03-15 10:57:46 +01:00
* error = [ NSError errorWithDomain : SSZipArchiveErrorDomain
code : SSZipArchiveErrorCodeFileContentNotReadable
2016-11-05 15:14:30 +01:00
userInfo : @{ NSLocalizedDescriptionKey : @" failed to read contents of file entry " } ] ;
}
}
return NO ;
}
return YES ;
}
unzCloseCurrentFile ( zip ) ;
ret = unzGoToNextFile ( zip ) ;
2017-02-24 13:32:28 +08:00
} while ( ret = = UNZ_OK & & UNZ_OK ! = UNZ_END_OF_LIST_OF_FILE ) ;
2016-11-05 15:14:30 +01:00
}
// No password required
return YES ;
}
2015-10-23 14:15:31 -04:00
# pragma mark - Unzipping
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination
{
2015-11-24 10:31:51 -08:00
return [ self unzipFileAtPath : path toDestination : destination delegate : nil ] ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination overwrite: ( BOOL ) overwrite password: ( nullable NSString * ) password error: ( NSError * * ) error
2015-10-23 14:15:31 -04:00
{
2016-07-13 14:11:29 +06:00
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : YES overwrite : overwrite password : password error : error delegate : nil progressHandler : nil completionHandler : nil ] ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination delegate: ( nullable id < SSZipArchiveDelegate > ) delegate
2015-10-23 14:15:31 -04:00
{
2016-07-13 14:11:29 +06:00
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : YES overwrite : YES password : nil error : nil delegate : delegate progressHandler : nil completionHandler : nil ] ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination overwrite: ( BOOL ) overwrite password: ( nullable NSString * ) password error: ( NSError * * ) error delegate: ( nullable id < SSZipArchiveDelegate > ) delegate
2015-10-23 14:15:31 -04:00
{
2016-07-13 14:11:29 +06:00
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : YES overwrite : overwrite password : password error : error delegate : delegate progressHandler : nil completionHandler : nil ] ;
2015-10-23 14:15:31 -04:00
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path
2015-11-24 10:31:51 -08:00
toDestination: ( NSString * ) destination
overwrite: ( BOOL ) overwrite
password: ( NSString * ) password
progressHandler: ( void ( ^ ) ( NSString * entry , unz_file_info zipInfo , long entryNumber , long total ) ) progressHandler
2017-09-13 15:54:43 +08:00
completionHandler: ( void ( ^ ) ( NSString * path , BOOL succeeded , NSError * __nullable error ) ) completionHandler
2015-10-23 14:15:31 -04:00
{
2016-07-13 14:11:29 +06:00
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : YES overwrite : overwrite password : password error : nil delegate : nil progressHandler : progressHandler completionHandler : completionHandler ] ;
2015-10-23 14:15:31 -04:00
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path
2015-11-24 10:31:51 -08:00
toDestination: ( NSString * ) destination
progressHandler: ( void ( ^ ) ( NSString * entry , unz_file_info zipInfo , long entryNumber , long total ) ) progressHandler
2016-11-09 10:55:14 +01:00
completionHandler: ( void ( ^ ) ( NSString * path , BOOL succeeded , NSError * __nullable error ) ) completionHandler
2015-10-23 14:15:31 -04:00
{
2016-07-13 14:11:29 +06:00
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : YES overwrite : YES password : nil error : nil delegate : nil progressHandler : progressHandler completionHandler : completionHandler ] ;
2015-10-23 14:15:31 -04:00
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path
2015-11-24 10:31:51 -08:00
toDestination: ( NSString * ) destination
2016-07-13 14:11:29 +06:00
preserveAttributes: ( BOOL ) preserveAttributes
overwrite: ( BOOL ) overwrite
password: ( nullable NSString * ) password
error: ( NSError * * ) error
delegate: ( nullable id < SSZipArchiveDelegate > ) delegate
{
return [ self unzipFileAtPath : path toDestination : destination preserveAttributes : preserveAttributes overwrite : overwrite password : password error : error delegate : delegate progressHandler : nil completionHandler : nil ] ;
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path
toDestination: ( NSString * ) destination
preserveAttributes: ( BOOL ) preserveAttributes
2015-11-24 10:31:51 -08:00
overwrite: ( BOOL ) overwrite
2017-08-01 16:07:22 -07:00
password: ( nullable NSString * ) password
2015-11-24 10:31:51 -08:00
error: ( NSError * * ) error
delegate: ( id < SSZipArchiveDelegate > ) delegate
progressHandler: ( void ( ^ ) ( NSString * entry , unz_file_info zipInfo , long entryNumber , long total ) ) progressHandler
2016-11-09 10:55:14 +01:00
completionHandler: ( void ( ^ ) ( NSString * path , BOOL succeeded , NSError * __nullable error ) ) completionHandler
2015-10-23 14:15:31 -04:00
{
2017-08-01 16:08:00 -07:00
// Guard against empty strings
2017-09-14 00:31:27 +08:00
if ( path . length = = 0 | | destination . length = = 0 )
2017-08-01 16:08:00 -07:00
{
NSDictionary * userInfo = @{ NSLocalizedDescriptionKey : @" received invalid argument(s) " } ;
NSError * err = [ NSError errorWithDomain : SSZipArchiveErrorDomain code : SSZipArchiveErrorCodeInvalidArguments userInfo : userInfo ] ;
if ( error )
{
* error = err ;
}
if ( completionHandler )
{
completionHandler ( nil , NO , err ) ;
}
2017-09-10 02:49:18 +08:00
return NO ;
2017-08-01 16:08:00 -07:00
}
2015-11-24 10:31:51 -08:00
// Begin opening
2017-09-14 00:31:27 +08:00
zipFile zip = unzOpen ( path . fileSystemRepresentation ) ;
2015-11-24 10:31:51 -08:00
if ( zip = = NULL )
{
NSDictionary * userInfo = @{ NSLocalizedDescriptionKey : @" failed to open zip file " } ;
2017-03-15 10:57:46 +01:00
NSError * err = [ NSError errorWithDomain : SSZipArchiveErrorDomain code : SSZipArchiveErrorCodeFailedOpenZipFile userInfo : userInfo ] ;
2015-11-24 10:31:51 -08:00
if ( error )
{
* error = err ;
}
if ( completionHandler )
{
completionHandler ( nil , NO , err ) ;
}
return NO ;
}
NSDictionary * fileAttributes = [ [ NSFileManager defaultManager ] attributesOfItemAtPath : path error : nil ] ;
unsigned long long fileSize = [ fileAttributes [ NSFileSize ] unsignedLongLongValue ] ;
unsigned long long currentPosition = 0 ;
unz_global_info globalInfo = { 0ul , 0ul } ;
unzGetGlobalInfo ( zip , & globalInfo ) ;
// Begin unzipping
if ( unzGoToFirstFile ( zip ) ! = UNZ_OK )
{
NSDictionary * userInfo = @{ NSLocalizedDescriptionKey : @" failed to open first file in zip file " } ;
2017-06-22 03:28:37 +08:00
NSError * err = [ NSError errorWithDomain : SSZipArchiveErrorDomain code : SSZipArchiveErrorCodeFailedOpenFileInZip userInfo : userInfo ] ;
2015-11-24 10:31:51 -08:00
if ( error )
{
* error = err ;
}
if ( completionHandler )
{
completionHandler ( nil , NO , err ) ;
}
return NO ;
}
BOOL success = YES ;
BOOL canceled = NO ;
int ret = 0 ;
2017-02-24 13:32:28 +08:00
int crc_ret = 0 ;
2015-11-24 10:31:51 -08:00
unsigned char buffer [ 4096 ] = { 0 } ;
NSFileManager * fileManager = [ NSFileManager defaultManager ] ;
2017-09-14 00:31:27 +08:00
NSMutableArray < NSDictionary * > * directoriesModificationDates = [ [ NSMutableArray alloc ] init ] ;
2015-11-24 10:31:51 -08:00
// Message delegate
if ( [ delegate respondsToSelector : @selector ( zipArchiveWillUnzipArchiveAtPath : zipInfo : ) ] ) {
[ delegate zipArchiveWillUnzipArchiveAtPath : path zipInfo : globalInfo ] ;
}
if ( [ delegate respondsToSelector : @selector ( zipArchiveProgressEvent : total : ) ] ) {
2016-01-27 09:00:58 -08:00
[ delegate zipArchiveProgressEvent : currentPosition total : fileSize ] ;
2015-11-24 10:31:51 -08:00
}
NSInteger currentFileNumber = 0 ;
2016-07-06 18:29:55 +06:00
NSError * unzippingError ;
2015-11-24 10:31:51 -08:00
do {
@autoreleasepool {
2017-09-14 00:31:27 +08:00
if ( password . length = = 0 ) {
2015-11-24 10:31:51 -08:00
ret = unzOpenCurrentFile ( zip ) ;
} else {
2017-09-16 18:01:56 +08:00
ret = unzOpenCurrentFilePassword ( zip , [ password cStringUsingEncoding : NSUTF8StringEncoding ] ) ;
2015-11-24 10:31:51 -08:00
}
if ( ret ! = UNZ_OK ) {
2017-06-22 03:28:37 +08:00
unzippingError = [ NSError errorWithDomain : @" SSZipArchiveErrorDomain " code : SSZipArchiveErrorCodeFailedOpenFileInZip userInfo : @{ NSLocalizedDescriptionKey : @" failed to open file in zip file " } ] ;
2015-11-24 10:31:51 -08:00
success = NO ;
break ;
}
// Reading data and write to file
unz_file_info fileInfo ;
memset ( & fileInfo , 0 , sizeof ( unz_file_info ) ) ;
ret = unzGetCurrentFileInfo ( zip , & fileInfo , NULL , 0 , NULL , 0 , NULL , 0 ) ;
if ( ret ! = UNZ_OK ) {
2017-06-22 03:28:37 +08:00
unzippingError = [ NSError errorWithDomain : @" SSZipArchiveErrorDomain " code : SSZipArchiveErrorCodeFileInfoNotLoadable userInfo : @{ NSLocalizedDescriptionKey : @" failed to retrieve info for file " } ] ;
2015-11-24 10:31:51 -08:00
success = NO ;
unzCloseCurrentFile ( zip ) ;
break ;
}
currentPosition + = fileInfo . compressed_size ;
// Message delegate
if ( [ delegate respondsToSelector : @selector ( zipArchiveShouldUnzipFileAtIndex : totalFiles : archivePath : fileInfo : ) ] ) {
if ( ! [ delegate zipArchiveShouldUnzipFileAtIndex : currentFileNumber
totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path fileInfo : fileInfo ] ) {
success = NO ;
canceled = YES ;
break ;
}
}
if ( [ delegate respondsToSelector : @selector ( zipArchiveWillUnzipFileAtIndex : totalFiles : archivePath : fileInfo : ) ] ) {
[ delegate zipArchiveWillUnzipFileAtIndex : currentFileNumber totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path fileInfo : fileInfo ] ;
}
if ( [ delegate respondsToSelector : @selector ( zipArchiveProgressEvent : total : ) ] ) {
[ delegate zipArchiveProgressEvent : ( NSInteger ) currentPosition total : ( NSInteger ) fileSize ] ;
}
char * filename = ( char * ) malloc ( fileInfo . size_filename + 1 ) ;
2015-11-26 10:47:55 -05:00
if ( filename = = NULL )
{
2017-02-24 14:04:41 +08:00
success = NO ;
break ;
2015-11-26 10:47:55 -05:00
}
2015-11-24 10:31:51 -08:00
unzGetCurrentFileInfo ( zip , & fileInfo , filename , fileInfo . size_filename + 1 , NULL , 0 , NULL , 0 ) ;
filename [ fileInfo . size_filename ] = ' \0 ' ;
//
// Determine whether this is a symbolic link:
// - File is stored with 'version made by' value of UNIX (3),
// as per http://www.pkware.com/documents/casestudies/APPNOTE.TXT
// in the upper byte of the version field.
// - BSD4.4 st_mode constants are stored in the high 16 bits of the
// external file attributes (defacto standard, verified against libarchive)
//
// The original constants can be found here:
// http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h
//
const uLong ZipUNIXVersion = 3 ;
const uLong BSD_SFMT = 0170000 ;
const uLong BSD_IFLNK = 0120000 ;
BOOL fileIsSymbolicLink = NO ;
if ( ( ( fileInfo . version > > 8 ) = = ZipUNIXVersion ) & & BSD_IFLNK = = ( BSD_SFMT & ( fileInfo . external_fa > > 16 ) ) ) {
2016-07-13 10:56:30 +06:00
fileIsSymbolicLink = YES ;
2015-11-24 10:31:51 -08:00
}
// Check if it contains directory
2016-08-10 14:44:44 +08:00
// NSString * strPath = @(filename);
2017-09-14 00:31:27 +08:00
NSString * strPath = @( filename ) ;
2016-08-10 14:44:44 +08:00
//if filename contains chinese dir transform Encoding
if ( ! strPath ) {
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding ( kCFStringEncodingGB_18030_2000 ) ;
strPath = [ NSString stringWithCString : filename encoding : enc ] ;
}
//end by skyfox
2015-11-24 10:31:51 -08:00
BOOL isDirectory = NO ;
if ( filename [ fileInfo . size_filename - 1 ] = = ' / ' | | filename [ fileInfo . size_filename - 1 ] = = ' \\ ' ) {
isDirectory = YES ;
}
free ( filename ) ;
// Contains a path
if ( [ strPath rangeOfCharacterFromSet : [ NSCharacterSet characterSetWithCharactersInString : @" / \\ " ] ] . location ! = NSNotFound ) {
strPath = [ strPath stringByReplacingOccurrencesOfString : @" \\ " withString : @" / " ] ;
}
2016-09-27 20:38:52 -07:00
NSString * fullPath = [ destination stringByAppendingPathComponent : strPath ] ;
2015-11-24 10:31:51 -08:00
NSError * err = nil ;
2016-07-13 14:11:29 +06:00
NSDictionary * directoryAttr ;
if ( preserveAttributes ) {
2017-06-22 12:06:35 +08:00
NSDate * modDate = [ [ self class ] _dateWithMSDOSFormat : ( UInt32 ) fileInfo . dos_date ] ;
2016-07-13 14:11:29 +06:00
directoryAttr = @{ NSFileCreationDate : modDate , NSFileModificationDate : modDate } ;
[ directoriesModificationDates addObject : @{ @" path " : fullPath , @" modDate " : modDate } ] ;
}
2015-11-24 10:31:51 -08:00
if ( isDirectory ) {
[ fileManager createDirectoryAtPath : fullPath withIntermediateDirectories : YES attributes : directoryAttr error : & err ] ;
} else {
2017-09-14 00:31:27 +08:00
[ fileManager createDirectoryAtPath : fullPath . stringByDeletingLastPathComponent withIntermediateDirectories : YES attributes : directoryAttr error : & err ] ;
2015-11-24 10:31:51 -08:00
}
if ( nil ! = err ) {
2016-07-06 18:29:55 +06:00
if ( [ err . domain isEqualToString : NSCocoaErrorDomain ] & &
err . code = = 640 ) {
unzippingError = err ;
unzCloseCurrentFile ( zip ) ;
success = NO ;
break ;
}
2015-11-24 10:31:51 -08:00
NSLog ( @" [SSZipArchive] Error: %@ " , err . localizedDescription ) ;
}
if ( [ fileManager fileExistsAtPath : fullPath ] & & ! isDirectory & & ! overwrite ) {
//FIXME: couldBe CRC Check?
unzCloseCurrentFile ( zip ) ;
ret = unzGoToNextFile ( zip ) ;
continue ;
}
if ( ! fileIsSymbolicLink ) {
2016-11-05 15:16:16 +01:00
// ensure we are not creating stale file entries
int readBytes = unzReadCurrentFile ( zip , buffer , 4096 ) ;
if ( readBytes > = 0 ) {
2017-09-14 00:31:27 +08:00
FILE * fp = fopen ( fullPath . fileSystemRepresentation , " wb " ) ;
2016-11-05 15:16:16 +01:00
while ( fp ) {
if ( readBytes > 0 ) {
2017-07-24 10:07:37 +04:00
if ( 0 = = fwrite ( buffer , readBytes , 1 , fp ) ) {
if ( ferror ( fp ) ) {
NSString * message = [ NSString stringWithFormat : @" Failed to write file (check your free space) " ] ;
NSLog ( @" [SSZipArchive] %@ " , message ) ;
success = NO ;
2017-08-01 10:17:37 -07:00
unzippingError = [ NSError errorWithDomain : @" SSZipArchiveErrorDomain " code : SSZipArchiveErrorCodeFailedToWriteFile userInfo : @{ NSLocalizedDescriptionKey : message } ] ;
2017-07-24 10:07:37 +04:00
break ;
}
}
2016-11-05 15:16:16 +01:00
} else {
break ;
2015-10-23 14:15:31 -04:00
}
2016-11-05 15:16:16 +01:00
readBytes = unzReadCurrentFile ( zip , buffer , 4096 ) ;
2015-10-23 14:15:31 -04:00
}
2016-07-13 14:11:29 +06:00
2016-11-05 15:16:16 +01:00
if ( fp ) {
2017-09-14 00:31:27 +08:00
if ( [ fullPath . pathExtension . lowercaseString isEqualToString : @" zip " ] ) {
NSLog ( @" Unzipping nested .zip file: %@ " , fullPath . lastPathComponent ) ;
if ( [ self unzipFileAtPath : fullPath toDestination : fullPath . stringByDeletingLastPathComponent overwrite : overwrite password : password error : nil delegate : nil ] ) {
2016-11-05 15:16:16 +01:00
[ [ NSFileManager defaultManager ] removeItemAtPath : fullPath error : nil ] ;
2015-11-24 10:31:51 -08:00
}
}
2016-07-13 14:11:29 +06:00
2016-11-05 15:16:16 +01:00
fclose ( fp ) ;
2016-07-13 14:11:29 +06:00
2016-11-05 15:16:16 +01:00
if ( preserveAttributes ) {
2016-07-13 14:11:29 +06:00
2016-11-05 15:16:16 +01:00
// Set the original datetime property
2017-06-22 12:06:35 +08:00
if ( fileInfo . dos_date ! = 0 ) {
NSDate * orgDate = [ [ self class ] _dateWithMSDOSFormat : ( UInt32 ) fileInfo . dos_date ] ;
2016-11-05 15:16:16 +01:00
NSDictionary * attr = @{ NSFileModificationDate : orgDate } ;
if ( attr ) {
2017-09-09 23:05:11 +08:00
if ( ! [ fileManager setAttributes : attr ofItemAtPath : fullPath error : nil ] ) {
2016-11-05 15:16:16 +01:00
// Can't set attributes
NSLog ( @" [SSZipArchive] Failed to set attributes - whilst setting modification date " ) ;
}
}
2016-07-13 14:11:29 +06:00
}
2017-07-12 13:17:07 +08:00
// Set the original permissions on the file (+read/write to solve #293)
uLong permissions = fileInfo . external_fa > > 16 | 0 b110000000 ;
2016-11-05 15:16:16 +01:00
if ( permissions ! = 0 ) {
// Store it into a NSNumber
NSNumber * permissionsValue = @( permissions ) ;
// Retrieve any existing attributes
NSMutableDictionary * attrs = [ [ NSMutableDictionary alloc ] initWithDictionary : [ fileManager attributesOfItemAtPath : fullPath error : nil ] ] ;
// Set the value in the attributes dict
attrs [ NSFilePosixPermissions ] = permissionsValue ;
// Update attributes
2017-09-09 23:05:11 +08:00
if ( ! [ fileManager setAttributes : attrs ofItemAtPath : fullPath error : nil ] ) {
2016-11-05 15:16:16 +01:00
// Unable to set the permissions attribute
NSLog ( @" [SSZipArchive] Failed to set attributes - whilst setting permissions " ) ;
}
}
2016-07-13 14:11:29 +06:00
}
2015-10-23 14:15:31 -04:00
}
2016-11-05 15:16:16 +01:00
else
{
// if we couldn't open file descriptor we can validate global errno to see the reason
if ( errno = = ENOSPC ) {
NSError * enospcError = [ NSError errorWithDomain : NSPOSIXErrorDomain
code : ENOSPC
userInfo : nil ] ;
unzippingError = enospcError ;
unzCloseCurrentFile ( zip ) ;
success = NO ;
break ;
}
2016-07-06 18:29:55 +06:00
}
}
2015-11-24 10:31:51 -08:00
}
2015-10-23 14:15:31 -04:00
else
{
// Assemble the path for the symbolic link
NSMutableString * destinationPath = [ NSMutableString string ] ;
int bytesRead = 0 ;
2017-02-24 13:32:28 +08:00
while ( ( bytesRead = unzReadCurrentFile ( zip , buffer , 4096 ) ) > 0 )
2015-10-23 14:15:31 -04:00
{
buffer [ bytesRead ] = ( int ) 0 ;
[ destinationPath appendString : @( ( const char * ) buffer ) ] ;
}
2015-11-24 10:31:51 -08:00
2017-06-22 12:57:32 -05:00
// Check if the symlink exists and delete it if we're overwriting
if ( overwrite )
{
if ( [ fileManager fileExistsAtPath : fullPath ] )
{
NSError * error = nil ;
2017-08-24 17:45:08 +01:00
BOOL removeSuccess = [ fileManager removeItemAtPath : fullPath error : & error ] ;
if ( ! removeSuccess )
2017-06-22 12:57:32 -05:00
{
NSString * message = [ NSString stringWithFormat : @" Failed to delete existing symbolic link at \" %@ \" " , error . localizedDescription ] ;
NSLog ( @" [SSZipArchive] %@ " , message ) ;
success = NO ;
unzippingError = [ NSError errorWithDomain : SSZipArchiveErrorDomain code : error . code userInfo : @{ NSLocalizedDescriptionKey : message } ] ;
}
}
}
2015-10-23 14:15:31 -04:00
// Create the symbolic link (making sure it stays relative if it was relative before)
int symlinkError = symlink ( [ destinationPath cStringUsingEncoding : NSUTF8StringEncoding ] ,
[ fullPath cStringUsingEncoding : NSUTF8StringEncoding ] ) ;
2015-11-24 10:31:51 -08:00
2017-02-24 13:32:28 +08:00
if ( symlinkError ! = 0 )
2015-10-23 14:15:31 -04:00
{
2017-06-22 12:57:32 -05:00
// Bubble the error up to the completion handler
NSString * message = [ NSString stringWithFormat : @" Failed to create symbolic link at \" %@ \" to \" %@ \" - symlink() error code: %d " , fullPath , destinationPath , errno ] ;
NSLog ( @" [SSZipArchive] %@ " , message ) ;
success = NO ;
unzippingError = [ NSError errorWithDomain : NSPOSIXErrorDomain code : symlinkError userInfo : @{ NSLocalizedDescriptionKey : message } ] ;
2015-10-23 14:15:31 -04:00
}
}
2015-11-24 10:31:51 -08:00
2017-02-24 13:32:28 +08:00
crc_ret = unzCloseCurrentFile ( zip ) ;
2015-11-24 10:31:51 -08:00
if ( crc_ret = = UNZ_CRCERROR ) {
//CRC ERROR
2015-12-15 15:16:02 +01:00
success = NO ;
break ;
2015-11-24 10:31:51 -08:00
}
2017-02-24 13:32:28 +08:00
ret = unzGoToNextFile ( zip ) ;
2015-11-24 10:31:51 -08:00
// Message delegate
if ( [ delegate respondsToSelector : @selector ( zipArchiveDidUnzipFileAtIndex : totalFiles : archivePath : fileInfo : ) ] ) {
[ delegate zipArchiveDidUnzipFileAtIndex : currentFileNumber totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path fileInfo : fileInfo ] ;
} else if ( [ delegate respondsToSelector : @selector ( zipArchiveDidUnzipFileAtIndex : totalFiles : archivePath : unzippedFilePath : ) ] ) {
[ delegate zipArchiveDidUnzipFileAtIndex : currentFileNumber totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path unzippedFilePath : fullPath ] ;
}
currentFileNumber + + ;
if ( progressHandler )
{
progressHandler ( strPath , fileInfo , currentFileNumber , globalInfo . number_entry ) ;
}
}
2017-09-09 23:05:11 +08:00
} while ( ret = = UNZ_OK & & success ) ;
2015-11-24 10:31:51 -08:00
// Close
unzClose ( zip ) ;
// The process of decompressing the .zip archive causes the modification times on the folders
2015-10-23 14:15:31 -04:00
// to be set to the present time. So, when we are done, they need to be explicitly set.
// set the modification date on all of the directories.
2016-07-13 14:11:29 +06:00
if ( success & & preserveAttributes ) {
2016-07-06 18:29:55 +06:00
NSError * err = nil ;
for ( NSDictionary * d in directoriesModificationDates ) {
if ( ! [ [ NSFileManager defaultManager ] setAttributes : @{ NSFileModificationDate : d [ @" modDate " ] } ofItemAtPath : d [ @" path " ] error : & err ] ) {
NSLog ( @" [SSZipArchive] Set attributes failed for directory: %@. " , d [ @" path " ] ) ;
}
if ( err ) {
NSLog ( @" [SSZipArchive] Error setting directory file modification date attribute: %@ " , err . localizedDescription ) ;
}
2015-10-23 14:15:31 -04:00
}
2016-07-06 18:29:55 +06:00
}
2015-11-24 10:31:51 -08:00
// Message delegate
if ( success & & [ delegate respondsToSelector : @selector ( zipArchiveDidUnzipArchiveAtPath : zipInfo : unzippedPath : ) ] ) {
[ delegate zipArchiveDidUnzipArchiveAtPath : path zipInfo : globalInfo unzippedPath : destination ] ;
}
// final progress event = 100%
2015-10-23 14:15:31 -04:00
if ( ! canceled & & [ delegate respondsToSelector : @selector ( zipArchiveProgressEvent : total : ) ] ) {
2015-11-24 10:31:51 -08:00
[ delegate zipArchiveProgressEvent : fileSize total : fileSize ] ;
}
2015-12-15 15:16:02 +01:00
NSError * retErr = nil ;
if ( crc_ret = = UNZ_CRCERROR )
{
NSDictionary * userInfo = @{ NSLocalizedDescriptionKey : @" crc check failed for file " } ;
2017-03-15 10:57:46 +01:00
retErr = [ NSError errorWithDomain : SSZipArchiveErrorDomain code : SSZipArchiveErrorCodeFileInfoNotLoadable userInfo : userInfo ] ;
2015-12-15 15:16:02 +01:00
}
2016-07-06 18:29:55 +06:00
if ( error ) {
if ( unzippingError ) {
* error = unzippingError ;
}
else {
* error = retErr ;
}
2015-12-15 15:16:02 +01:00
}
2015-11-24 10:31:51 -08:00
if ( completionHandler )
{
2016-07-06 18:29:55 +06:00
if ( unzippingError ) {
completionHandler ( path , success , unzippingError ) ;
}
else
{
completionHandler ( path , success , retErr ) ;
}
2015-11-24 10:31:51 -08:00
}
return success ;
2015-10-23 14:15:31 -04:00
}
# pragma mark - Zipping
2017-09-14 00:31:27 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withFilesAtPaths: ( NSArray < NSString * > * ) paths
2015-10-23 14:15:31 -04:00
{
2015-11-24 10:31:51 -08:00
return [ SSZipArchive createZipFileAtPath : path withFilesAtPaths : paths withPassword : nil ] ;
}
2017-09-12 21:15:13 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath {
2015-11-24 10:31:51 -08:00
return [ SSZipArchive createZipFileAtPath : path withContentsOfDirectory : directoryPath withPassword : nil ] ;
}
2015-10-23 14:15:31 -04:00
2017-09-12 21:15:13 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath keepParentDirectory: ( BOOL ) keepParentDirectory {
2016-05-31 11:25:15 -07:00
return [ SSZipArchive createZipFileAtPath : path withContentsOfDirectory : directoryPath keepParentDirectory : keepParentDirectory withPassword : nil ] ;
2015-11-24 10:31:51 -08:00
}
2017-09-14 00:31:27 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withFilesAtPaths: ( NSArray < NSString * > * ) paths withPassword: ( NSString * ) password
2015-11-24 10:31:51 -08:00
{
SSZipArchive * zipArchive = [ [ SSZipArchive alloc ] initWithPath : path ] ;
2017-09-12 21:15:13 +08:00
BOOL success = [ zipArchive open ] ;
if ( success ) {
2015-11-24 10:31:51 -08:00
for ( NSString * filePath in paths ) {
2017-09-12 21:15:13 +08:00
success & = [ zipArchive writeFile : filePath withPassword : password ] ;
2015-11-24 10:31:51 -08:00
}
2017-09-12 21:15:13 +08:00
success & = [ zipArchive close ] ;
2015-11-24 10:31:51 -08:00
}
return success ;
2015-10-23 14:15:31 -04:00
}
2017-09-12 21:15:13 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath withPassword: ( nullable NSString * ) password {
2017-01-23 19:45:35 +00:00
return [ SSZipArchive createZipFileAtPath : path withContentsOfDirectory : directoryPath keepParentDirectory : NO withPassword : password ] ;
2015-10-23 14:15:31 -04:00
}
2017-09-12 21:15:13 +08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath keepParentDirectory: ( BOOL ) keepParentDirectory withPassword: ( nullable NSString * ) password {
2017-01-23 19:45:35 +00:00
return [ SSZipArchive createZipFileAtPath : path
2017-02-24 13:32:28 +08:00
withContentsOfDirectory : directoryPath
keepParentDirectory : keepParentDirectory
withPassword : password
andProgressHandler : nil
2017-01-23 19:45:35 +00:00
] ;
}
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath keepParentDirectory: ( BOOL ) keepParentDirectory withPassword: ( nullable NSString * ) password andProgressHandler: ( void ( ^ _Nullable ) ( NSUInteger entryNumber , NSUInteger total ) ) progressHandler {
2015-10-23 14:15:31 -04:00
SSZipArchive * zipArchive = [ [ SSZipArchive alloc ] initWithPath : path ] ;
2017-09-12 21:15:13 +08:00
BOOL success = [ zipArchive open ] ;
if ( success ) {
2017-09-09 23:05:11 +08:00
// use a local fileManager (queue/thread compatibility)
NSFileManager * fileManager = [ [ NSFileManager alloc ] init ] ;
2015-10-23 14:15:31 -04:00
NSDirectoryEnumerator * dirEnumerator = [ fileManager enumeratorAtPath : directoryPath ] ;
2017-09-14 00:31:27 +08:00
NSArray < NSString * > * allObjects = dirEnumerator . allObjects ;
2017-01-23 19:45:35 +00:00
NSUInteger total = allObjects . count , complete = 0 ;
2015-10-23 14:15:31 -04:00
NSString * fileName ;
2017-01-23 19:45:35 +00:00
for ( fileName in allObjects ) {
2015-10-23 14:15:31 -04:00
BOOL isDir ;
NSString * fullFilePath = [ directoryPath stringByAppendingPathComponent : fileName ] ;
[ fileManager fileExistsAtPath : fullFilePath isDirectory : & isDir ] ;
2016-03-23 16:54:24 -07:00
if ( keepParentDirectory )
{
2017-09-14 00:31:27 +08:00
fileName = [ directoryPath . lastPathComponent stringByAppendingPathComponent : fileName ] ;
2016-03-23 16:54:24 -07:00
}
2015-10-23 14:15:31 -04:00
if ( ! isDir ) {
2017-09-12 21:15:13 +08:00
success & = [ zipArchive writeFileAtPath : fullFilePath withFileName : fileName withPassword : password ] ;
2015-10-23 14:15:31 -04:00
}
else
{
2017-02-24 13:32:28 +08:00
if ( [ [ NSFileManager defaultManager ] subpathsOfDirectoryAtPath : fullFilePath error : nil ] . count = = 0 )
2015-10-23 14:15:31 -04:00
{
2016-05-11 16:39:08 +01:00
NSString * tempFilePath = [ self _temporaryPathForDiscardableFile ] ;
NSString * tempFileFilename = [ fileName stringByAppendingPathComponent : tempFilePath . lastPathComponent ] ;
2017-09-12 21:15:13 +08:00
success & = [ zipArchive writeFileAtPath : tempFilePath withFileName : tempFileFilename withPassword : password ] ;
2015-10-23 14:15:31 -04:00
}
}
2017-01-23 19:45:35 +00:00
complete + + ;
if ( progressHandler ) {
progressHandler ( complete , total ) ;
}
2015-10-23 14:15:31 -04:00
}
2017-09-12 21:15:13 +08:00
success & = [ zipArchive close ] ;
2015-10-23 14:15:31 -04:00
}
return success ;
}
2017-09-14 00:31:27 +08:00
- ( instancetype ) init { @throw nil ; }
2015-10-23 14:15:31 -04:00
- ( instancetype ) initWithPath: ( NSString * ) path
{
2015-11-24 10:31:51 -08:00
if ( ( self = [ super init ] ) ) {
_path = [ path copy ] ;
}
return self ;
2015-10-23 14:15:31 -04:00
}
- ( BOOL ) open
{
2015-11-24 10:31:51 -08:00
NSAssert ( ( _zip = = NULL ) , @" Attempting open an archive which is already open " ) ;
2017-09-14 00:31:27 +08:00
_zip = zipOpen ( _path . fileSystemRepresentation , APPEND_STATUS_CREATE ) ;
2015-11-24 10:31:51 -08:00
return ( NULL ! = _zip ) ;
2015-10-23 14:15:31 -04:00
}
- ( void ) zipInfo: ( zip_fileinfo * ) zipInfo setDate: ( NSDate * ) date
{
NSCalendar * currentCalendar = [ NSCalendar currentCalendar ] ;
# if defined(__IPHONE_8_0) || defined(__MAC_10_10)
2017-09-09 23:05:11 +08:00
NSCalendarUnit flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ;
2015-10-23 14:15:31 -04:00
# else
uint flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ;
# endif
NSDateComponents * components = [ currentCalendar components : flags fromDate : date ] ;
2017-06-22 12:06:35 +08:00
struct tm tmz_date ;
tmz_date . tm_sec = ( unsigned int ) components . second ;
tmz_date . tm_min = ( unsigned int ) components . minute ;
tmz_date . tm_hour = ( unsigned int ) components . hour ;
tmz_date . tm_mday = ( unsigned int ) components . day ;
tmz_date . tm_mon = ( unsigned int ) components . month - 1 ;
tmz_date . tm_year = ( unsigned int ) components . year ;
zipInfo - > dos_date = tm_to_dosdate ( & tmz_date ) ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
- ( BOOL ) writeFolderAtPath: ( NSString * ) path withFolderName: ( NSString * ) folderName withPassword: ( nullable NSString * ) password
2015-10-23 14:15:31 -04:00
{
NSAssert ( ( _zip ! = NULL ) , @" Attempting to write to an archive which was never opened " ) ;
2015-11-24 10:31:51 -08:00
2017-06-22 12:06:35 +08:00
zip_fileinfo zipInfo = { 0 , 0 , 0 } ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
NSDictionary * attr = [ [ NSFileManager defaultManager ] attributesOfItemAtPath : path error : nil ] ;
2017-02-24 13:32:28 +08:00
if ( attr )
2015-10-23 14:15:31 -04:00
{
NSDate * fileDate = ( NSDate * ) attr [ NSFileModificationDate ] ;
2017-02-24 13:32:28 +08:00
if ( fileDate )
2015-10-23 14:15:31 -04:00
{
2017-02-24 13:32:28 +08:00
[ self zipInfo : & zipInfo setDate : fileDate ] ;
2015-10-23 14:15:31 -04:00
}
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
// Get the permissions value from the files attributes
NSNumber * permissionsValue = ( NSNumber * ) attr [ NSFilePosixPermissions ] ;
2017-09-09 14:01:27 +08:00
if ( permissionsValue ! = nil ) {
2015-10-23 14:15:31 -04:00
// Get the short value for the permissions
short permissionsShort = permissionsValue . shortValue ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
NSInteger permissionsOctal = 0100000 + permissionsShort ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Convert this into a long value
uLong permissionsLong = @( permissionsOctal ) . unsignedLongValue ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
2017-07-19 12:33:46 -07:00
2017-09-10 02:49:18 +08:00
// Casted back to an unsigned int to match type of external_fa in minizip
2017-07-19 12:33:46 -07:00
zipInfo . external_fa = ( unsigned int ) ( permissionsLong < < 16L ) ;
2015-10-23 14:15:31 -04:00
}
}
2015-11-24 10:31:51 -08:00
unsigned int len = 0 ;
2017-09-14 00:31:27 +08:00
zipOpenNewFileInZip3 ( _zip , [ folderName stringByAppendingString : @" / " ] . fileSystemRepresentation , & zipInfo , NULL , 0 , NULL , 0 , NULL , Z_DEFLATED , Z_NO_COMPRESSION , 0 , - MAX_WBITS , DEF_MEM_LEVEL ,
Z_DEFAULT_STRATEGY , password . UTF8String , 0 ) ;
2015-11-24 10:31:51 -08:00
zipWriteInFileInZip ( _zip , & len , 0 ) ;
zipCloseFileInZip ( _zip ) ;
return YES ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
- ( BOOL ) writeFile: ( NSString * ) path withPassword: ( nullable NSString * ) password ;
2015-10-23 14:15:31 -04:00
{
2015-11-24 10:31:51 -08:00
return [ self writeFileAtPath : path withFileName : nil withPassword : password ] ;
2015-10-23 14:15:31 -04:00
}
// supports writing files with logical folder/directory structure
// *path* is the absolute path of the file that will be compressed
// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt
2016-07-05 15:12:27 -04:00
- ( BOOL ) writeFileAtPath : ( NSString * ) path withFileName : ( nullable NSString * ) fileName withPassword : ( nullable NSString * ) password
2015-10-23 14:15:31 -04:00
{
NSAssert ( ( _zip ! = NULL ) , @" Attempting to write to an archive which was never opened " ) ;
2015-11-24 10:31:51 -08:00
2017-09-14 00:31:27 +08:00
FILE * input = fopen ( path . fileSystemRepresentation , " r " ) ;
2015-11-24 10:31:51 -08:00
if ( NULL = = input ) {
return NO ;
}
2017-09-09 23:05:11 +08:00
const char * aFileName ;
2015-10-23 14:15:31 -04:00
if ( ! fileName ) {
2017-09-14 00:31:27 +08:00
aFileName = path . lastPathComponent . fileSystemRepresentation ;
2015-10-23 14:15:31 -04:00
}
else {
2017-09-14 00:31:27 +08:00
aFileName = fileName . fileSystemRepresentation ;
2015-10-23 14:15:31 -04:00
}
2015-11-24 10:31:51 -08:00
2017-06-22 12:06:35 +08:00
zip_fileinfo zipInfo = { 0 , 0 , 0 } ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
NSDictionary * attr = [ [ NSFileManager defaultManager ] attributesOfItemAtPath : path error : nil ] ;
2017-02-24 13:32:28 +08:00
if ( attr )
2015-10-23 14:15:31 -04:00
{
NSDate * fileDate = ( NSDate * ) attr [ NSFileModificationDate ] ;
2017-02-24 13:32:28 +08:00
if ( fileDate )
2015-10-23 14:15:31 -04:00
{
2017-02-24 13:32:28 +08:00
[ self zipInfo : & zipInfo setDate : fileDate ] ;
2015-10-23 14:15:31 -04:00
}
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
// Get the permissions value from the files attributes
NSNumber * permissionsValue = ( NSNumber * ) attr [ NSFilePosixPermissions ] ;
2017-09-09 14:01:27 +08:00
if ( permissionsValue ! = nil ) {
2015-10-23 14:15:31 -04:00
// Get the short value for the permissions
short permissionsShort = permissionsValue . shortValue ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
NSInteger permissionsOctal = 0100000 + permissionsShort ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Convert this into a long value
uLong permissionsLong = @( permissionsOctal ) . unsignedLongValue ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
2017-07-19 12:33:46 -07:00
2017-09-10 02:49:18 +08:00
// Casted back to an unsigned int to match type of external_fa in minizip
2017-07-19 12:33:46 -07:00
zipInfo . external_fa = ( unsigned int ) ( permissionsLong < < 16L ) ;
2015-10-23 14:15:31 -04:00
}
}
2015-11-24 10:31:51 -08:00
void * buffer = malloc ( CHUNK ) ;
2015-11-26 10:47:55 -05:00
if ( buffer = = NULL )
{
return NO ;
}
2017-09-13 15:54:43 +08:00
2017-09-14 00:31:27 +08:00
zipOpenNewFileInZip3 ( _zip , aFileName , & zipInfo , NULL , 0 , NULL , 0 , NULL , Z_DEFLATED , Z_DEFAULT_COMPRESSION , 0 , - MAX_WBITS , DEF_MEM_LEVEL , Z_DEFAULT_STRATEGY , password . UTF8String , 0 ) ;
2015-11-24 10:31:51 -08:00
unsigned int len = 0 ;
2017-02-24 13:32:28 +08:00
while ( ! feof ( input ) & & ! ferror ( input ) )
2015-10-23 14:15:31 -04:00
{
2015-11-24 10:31:51 -08:00
len = ( unsigned int ) fread ( buffer , 1 , CHUNK , input ) ;
zipWriteInFileInZip ( _zip , buffer , len ) ;
}
zipCloseFileInZip ( _zip ) ;
free ( buffer ) ;
fclose ( input ) ;
return YES ;
2015-10-23 14:15:31 -04:00
}
2016-07-05 15:12:27 -04:00
- ( BOOL ) writeData : ( NSData * ) data filename : ( nullable NSString * ) filename withPassword : ( nullable NSString * ) password ;
2015-10-23 14:15:31 -04:00
{
if ( ! _zip ) {
2015-11-24 10:31:51 -08:00
return NO ;
2015-10-23 14:15:31 -04:00
}
if ( ! data ) {
2015-11-24 10:31:51 -08:00
return NO ;
2015-10-23 14:15:31 -04:00
}
2017-06-22 12:06:35 +08:00
zip_fileinfo zipInfo = { 0 , 0 , 0 } ;
2015-10-23 14:15:31 -04:00
[ self zipInfo : & zipInfo setDate : [ NSDate date ] ] ;
2015-11-24 10:31:51 -08:00
2017-09-14 00:31:27 +08:00
zipOpenNewFileInZip3 ( _zip , filename . fileSystemRepresentation , & zipInfo , NULL , 0 , NULL , 0 , NULL , Z_DEFLATED , Z_DEFAULT_COMPRESSION , 0 , - MAX_WBITS , DEF_MEM_LEVEL , Z_DEFAULT_STRATEGY , password . UTF8String , 0 ) ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
zipWriteInFileInZip ( _zip , data . bytes , ( unsigned int ) data . length ) ;
2015-11-24 10:31:51 -08:00
zipCloseFileInZip ( _zip ) ;
return YES ;
2015-10-23 14:15:31 -04:00
}
- ( BOOL ) close
{
2015-11-24 10:31:51 -08:00
NSAssert ( ( _zip ! = NULL ) , @" [SSZipArchive] Attempting to close an archive which was never opened " ) ;
2017-09-10 03:07:15 +08:00
int error = zipClose ( _zip , NULL ) ;
_zip = nil ;
return error = = UNZ_OK ;
2015-10-23 14:15:31 -04:00
}
# pragma mark - Private
2016-05-11 21:51:06 -07:00
+ ( NSString * ) _temporaryPathForDiscardableFile
{
static NSString * discardableFileName = @" .DS_Store " ;
static NSString * discardableFilePath = nil ;
static dispatch_once_t onceToken ;
dispatch_once ( & onceToken , ^ {
2017-09-14 00:31:27 +08:00
NSString * temporaryDirectoryName = [ NSUUID UUID ] . UUIDString ;
2016-05-11 21:51:06 -07:00
NSString * temporaryDirectory = [ NSTemporaryDirectory ( ) stringByAppendingPathComponent : temporaryDirectoryName ] ;
BOOL directoryCreated = [ [ NSFileManager defaultManager ] createDirectoryAtPath : temporaryDirectory withIntermediateDirectories : YES attributes : nil error : nil ] ;
2017-01-28 22:47:43 +08:00
if ( directoryCreated ) {
discardableFilePath = [ temporaryDirectory stringByAppendingPathComponent : discardableFileName ] ;
[ @" " writeToFile : discardableFilePath atomically : YES encoding : NSUTF8StringEncoding error : nil ] ;
}
2016-05-11 21:51:06 -07:00
} ) ;
return discardableFilePath ;
}
2015-10-23 14:15:31 -04:00
// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
// YYYYYYY is years from 1980 = 0
// sssss is (seconds/2).
//
// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24
2015-10-08 15:29:34 +09:00
// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 3 = 14:33:06
2015-10-23 14:15:31 -04:00
+ ( NSDate * ) _dateWithMSDOSFormat : ( UInt32 ) msdosDateTime
{
2015-11-24 10:31:51 -08:00
static const UInt32 kYearMask = 0xFE000000 ;
static const UInt32 kMonthMask = 0x1E00000 ;
static const UInt32 kDayMask = 0x1F0000 ;
static const UInt32 kHourMask = 0xF800 ;
static const UInt32 kMinuteMask = 0x7E0 ;
static const UInt32 kSecondMask = 0x1F ;
static NSCalendar * gregorian ;
static dispatch_once_t onceToken ;
dispatch_once ( & onceToken , ^ {
2015-10-23 14:15:31 -04:00
# if defined(__IPHONE_8_0) || defined(__MAC_10_10)
2015-11-24 10:31:51 -08:00
gregorian = [ [ NSCalendar alloc ] initWithCalendarIdentifier : NSCalendarIdentifierGregorian ] ;
2015-10-23 14:15:31 -04:00
# else
gregorian = [ [ NSCalendar alloc ] initWithCalendarIdentifier : NSGregorianCalendar ] ;
# endif
2015-11-24 10:31:51 -08:00
} ) ;
2015-10-23 14:15:31 -04:00
NSDateComponents * components = [ [ NSDateComponents alloc ] init ] ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
NSAssert ( 0xFFFFFFFF = = ( kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask ) , @" [SSZipArchive] MSDOS date masks don't add up " ) ;
2015-11-24 10:31:51 -08:00
2017-09-14 00:31:27 +08:00
components . year = 1980 + ( ( msdosDateTime & kYearMask ) > > 25 ) ;
components . month = ( msdosDateTime & kMonthMask ) > > 21 ;
components . day = ( msdosDateTime & kDayMask ) > > 16 ;
components . hour = ( msdosDateTime & kHourMask ) > > 11 ;
components . minute = ( msdosDateTime & kMinuteMask ) > > 5 ;
components . second = ( msdosDateTime & kSecondMask ) * 2 ;
2015-11-24 10:31:51 -08:00
2015-10-23 14:15:31 -04:00
NSDate * date = [ NSDate dateWithTimeInterval : 0 sinceDate : [ gregorian dateFromComponents : components ] ] ;
2015-11-24 10:31:51 -08:00
return date ;
2015-10-23 14:15:31 -04:00
}
2017-01-23 19:45:35 +00:00
@end