123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /**
- // ZipArchive.m
- //
- //
- // Created by aish on 08-9-11.
- // acsolu@gmail.com
- // Copyright 2008 Inc. All rights reserved.
- //
- */
- #import "ZipArchive.h"
- #import "zlib.h"
- #import "zconf.h"
- #include "minizip/zip.h"
- #include "minizip/unzip.h"
- @interface NSFileManager(ZipArchive)
- - (NSDictionary *)_attributesOfItemAtPath:(NSString *)path followingSymLinks:(BOOL)followingSymLinks error:(NSError **)error;
- @end
- @interface ZipArchive ()
- -(void) OutputErrorMessage:(NSString*) msg;
- -(BOOL) OverWrite:(NSString*) file;
- -(NSDate*) Date1980;
- @property (nonatomic,copy) NSString* password;
- @end
- @implementation ZipArchive
- @synthesize delegate = _delegate;
- @synthesize numFiles = _numFiles;
- @synthesize password = _password;
- @synthesize unzippedFiles = _unzippedFiles;
- @synthesize progressBlock = _progressBlock;
- @synthesize stringEncoding = _stringEncoding;
- -(id) init
- {
- return [self initWithFileManager:[NSFileManager defaultManager]];
- }
- -(id) initWithFileManager:(NSFileManager*) fileManager
- {
- if( self=[super init] )
- {
- _zipFile = NULL;
- _fileManager = fileManager;
- self.stringEncoding = NSUTF8StringEncoding;
- self.compression = ZipArchiveCompressionDefault;
- }
- return self;
- }
- -(void) dealloc
- {
- // close any open file operations
- [self CloseZipFile2];
- [self UnzipCloseFile];
-
- // release retained/copied properties.
- [_password release];
- [_delegate release];
- [_unzippedFiles release];
-
- [super dealloc];
- }
- /**
- * Create a new zip file at the specified path, ready for new files to be added.
- *
- * @param zipFile the path of the zip file to create
- * @returns BOOL YES on success
- */
- -(BOOL) CreateZipFile2:(NSString*) zipFile
- {
- return [self CreateZipFile2:zipFile append:NO];
- }
- -(BOOL) CreateZipFile2:(NSString*) zipFile append:(BOOL)isAppend
- {
- _zipFile = zipOpen( (const char*)[zipFile UTF8String], (isAppend ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE) );
- if( !_zipFile )
- return NO;
- return YES;
- }
- /**
- * Create a new zip file at the specified path, ready for new files to be added.
- *
- * @param zipFile the path of the zip file to create
- * @param password a password used to encrypt the zip file
- * @returns BOOL YES on success
- */
- -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password
- {
- self.password = password;
- return [self CreateZipFile2:zipFile];
- }
- -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password append:(BOOL)isAppend
- {
- self.password = password;
- return [self CreateZipFile2:zipFile append:isAppend];
- }
- /**
- * add an existing file on disk to the zip archive, compressing it.
- *
- * @param file the path to the file to compress
- * @param newname the name of the file in the zip archive, ie: path relative to the zip archive root.
- * @returns BOOL YES on success
- */
- -(BOOL) addFileToZip:(NSString*) file newname:(NSString*) newname;
- {
- NSData *data = [NSData dataWithContentsOfFile:file];
- NSError* error = nil;
- NSDictionary* attr = [_fileManager _attributesOfItemAtPath:file followingSymLinks:YES error:&error];
- BOOL result = [self addDataToZip:data fileAttributes:attr newname:newname];
- return result;
- }
- /**
- * add an existing file on disk to the zip archive, compressing it.
- *
- * @param data the data to compress
- * @param attr the file attribute for data to add as file
- * @param newname the name of the file in the zip archive, ie: path relative to the zip archive root.
- * @returns BOOL YES on success
- */
- -(BOOL) addDataToZip:(NSData*) data fileAttributes:(NSDictionary *)attr newname:(NSString*) newname
- {
- if (!data)
- {
- return NO;
- }
- if( !_zipFile )
- return NO;
- // tm_zip filetime;
-
- zip_fileinfo zipInfo = {{0}};
- NSDate* fileDate = nil;
-
- if( attr )
- fileDate = (NSDate*)[attr objectForKey:NSFileModificationDate];
- if( fileDate == nil )
- fileDate = [NSDate date];
-
- NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSDateComponents* components = [gregorianCalendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay |
- NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:fileDate];
- [gregorianCalendar release];
-
- zipInfo.tmz_date.tm_sec = (uInt)components.second;
- zipInfo.tmz_date.tm_min = (uInt)components.minute;
- zipInfo.tmz_date.tm_hour = (uInt)components.hour;
- zipInfo.tmz_date.tm_mday = (uInt)components.day;
- zipInfo.tmz_date.tm_mon = (uInt)components.month;
- zipInfo.tmz_date.tm_year = (uInt)components.year;
-
-
- int ret ;
- if( [_password length] == 0 )
- {
- ret = zipOpenNewFileInZip( _zipFile,
- (const char*) [newname cStringUsingEncoding:self.stringEncoding],
- &zipInfo,
- NULL,0,
- NULL,0,
- NULL,//comment
- Z_DEFLATED,
- self.compression );
- }
- else
- {
- uLong crcValue = crc32( 0L,NULL, 0L );
- crcValue = crc32( crcValue, (const Bytef*)[data bytes], (unsigned int)[data length] );
- ret = zipOpenNewFileInZip3( _zipFile,
- (const char*) [newname cStringUsingEncoding:self.stringEncoding],
- &zipInfo,
- NULL,0,
- NULL,0,
- NULL,//comment
- Z_DEFLATED,
- self.compression,
- 0,
- 15,
- 8,
- Z_DEFAULT_STRATEGY,
- [_password cStringUsingEncoding:NSASCIIStringEncoding],
- crcValue );
- }
- if( ret!=Z_OK )
- {
- return NO;
- }
- unsigned int dataLen = (unsigned int)[data length];
- ret = zipWriteInFileInZip( _zipFile, (const void*)[data bytes], dataLen);
- if( ret!=Z_OK )
- {
- return NO;
- }
- ret = zipCloseFileInZip( _zipFile );
- if( ret!=Z_OK )
- return NO;
- return YES;
- }
- /**
- * Close a zip file after creating and added files to it.
- *
- * @returns BOOL YES on success
- */
- -(BOOL) CloseZipFile2
- {
- self.password = nil;
- if( _zipFile==NULL )
- return NO;
- BOOL ret = zipClose( _zipFile,NULL )==Z_OK?YES:NO;
- _zipFile = NULL;
- return ret;
- }
- /**
- * open an existing zip file ready for expanding.
- *
- * @param zipFile the path to a zip file to be opened.
- * @returns BOOL YES on success
- */
- -(BOOL) UnzipOpenFile:(NSString*) zipFile
- {
- // create an array to receive the list of unzipped files.
- if (_unzippedFiles) [_unzippedFiles release];
- _unzippedFiles = [[NSMutableArray alloc] initWithCapacity:1];
-
- _unzFile = unzOpen( (const char*)[zipFile UTF8String] );
- if( _unzFile )
- {
- unz_global_info globalInfo = {0};
- if( unzGetGlobalInfo(_unzFile, &globalInfo )==UNZ_OK )
- {
- _numFiles = globalInfo.number_entry;
- NSLog(@"%lu entries in the zip file", globalInfo.number_entry);
- }
- }
- return _unzFile!=NULL;
- }
- /**
- * open an existing zip file with a password ready for expanding.
- *
- * @param zipFile the path to a zip file to be opened.
- * @param password the password to use decrpyting the file.
- * @returns BOOL YES on success
- */
- -(BOOL) UnzipOpenFile:(NSString*) zipFile Password:(NSString*) password
- {
- self.password = password;
- return [self UnzipOpenFile:zipFile];
- }
- /**
- * Expand all files in the zip archive into the specified directory.
- *
- * If a delegate has been set and responds to OverWriteOperation: it can
- * return YES to overwrite a file, or NO to skip that file.
- *
- * On completion, the property `unzippedFiles` will be an array populated
- * with the full paths of each file that was successfully expanded.
- *
- * @param path the directory where expanded files will be created
- * @param overwrite should existing files be overwritten
- * @returns BOOL YES on success
- */
- -(BOOL) UnzipFileTo:(NSString*) path overWrite:(BOOL) overwrite
- {
- BOOL success = YES;
- int index = 0;
- int progress = -1;
- int ret = unzGoToFirstFile( _unzFile );
- unsigned char buffer[4096] = {0};
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Failed"];
- }
-
- const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
-
- do{
- @autoreleasepool {
- if( [_password length]==0 )
- ret = unzOpenCurrentFile( _unzFile );
- else
- ret = unzOpenCurrentFilePassword( _unzFile, password );
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occurs"];
- success = NO;
- break;
- }
- // reading data and write to file
- int read ;
- unz_file_info fileInfo ={0};
- ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occurs while getting file info"];
- success = NO;
- unzCloseCurrentFile( _unzFile );
- break;
- }
- char* filename = (char*) malloc( fileInfo.size_filename +1 );
- unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
- filename[fileInfo.size_filename] = '\0';
-
- // check if it contains directory
- NSString * strPath = [NSString stringWithCString:filename encoding:self.stringEncoding];
- BOOL isDirectory = NO;
- if( filename[fileInfo.size_filename-1]=='/' || filename[fileInfo.size_filename-1]=='\\')
- isDirectory = YES;
- free( filename );
- if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
- {// contains a path
- strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
- }
- NSString* fullPath = [path stringByAppendingPathComponent:strPath];
-
- if( isDirectory )
- [_fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:nil];
- else
- [_fileManager createDirectoryAtPath:[fullPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
-
- FILE* fp = NULL;
- do
- {
- read = unzReadCurrentFile(_unzFile, buffer, 4096);
- if (read >= 0)
- {
- if (fp == NULL) {
- if( [_fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite )
- {
- if( ![self OverWrite:fullPath] )
- {
- // don't process any more of the file, but continue
- break;
- }
- }
- if (!isDirectory) {
- fp = fopen( (const char*)[fullPath UTF8String], "wb");
- if (fp == NULL) {
- [self OutputErrorMessage:@"Failed to open output file for writing"];
- break;
- }
- }
- }
- fwrite(buffer, read, 1, fp );
- }
- else // if (read < 0)
- {
- ret = read; // result will be an error code
- success = NO;
- [self OutputErrorMessage:@"Failed to read zip file"];
- }
- } while (read > 0);
-
- if (fp)
- {
- fclose( fp );
-
- // add the full path of this file to the output array
- [(NSMutableArray*)_unzippedFiles addObject:fullPath];
-
- // set the orignal datetime property
- if( fileInfo.tmu_date.tm_year!=0 )
- {
- NSDateComponents* components = [[NSDateComponents alloc] init];
- components.second = fileInfo.tmu_date.tm_sec;
- components.minute = fileInfo.tmu_date.tm_min;
- components.hour = fileInfo.tmu_date.tm_hour;
- components.day = fileInfo.tmu_date.tm_mday;
- components.month = fileInfo.tmu_date.tm_mon + 1;
- components.year = fileInfo.tmu_date.tm_year;
-
- NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSDate* orgDate = [[gregorianCalendar dateFromComponents:components] retain];
- [components release];
- [gregorianCalendar release];
-
- NSDictionary* attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate]; //[_fileManager fileAttributesAtPath:fullPath traverseLink:YES];
- if( attr )
- {
- // [attr setValue:orgDate forKey:NSFileCreationDate];
- if( ![_fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] )
- {
- // cann't set attributes
- NSLog(@"Failed to set attributes");
- }
-
- }
- [orgDate release];
- orgDate = nil;
- }
-
- }
-
- if (ret == UNZ_OK) {
- ret = unzCloseCurrentFile( _unzFile );
- if (ret != UNZ_OK) {
- [self OutputErrorMessage:@"file was unzipped but failed crc check"];
- success = NO;
- }
- }
-
- if (ret == UNZ_OK) {
- ret = unzGoToNextFile( _unzFile );
- }
-
- if (_progressBlock && _numFiles) {
- index++;
- int p = index*100/_numFiles;
- progress = p;
- _progressBlock(progress, index, _numFiles);
- }
- }
- } while (ret==UNZ_OK && ret!=UNZ_END_OF_LIST_OF_FILE);
- return success;
- }
- -(NSDictionary *)UnzipFileToMemory
- {
- NSMutableDictionary *fileDictionary = [NSMutableDictionary dictionary];
-
- BOOL success = YES;
- int index = 0;
- int progress = -1;
- int ret = unzGoToFirstFile( _unzFile );
- unsigned char buffer[4096] = {0};
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Failed"];
- }
-
- const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
-
- do{
- @autoreleasepool {
- if( [_password length]==0 )
- ret = unzOpenCurrentFile( _unzFile );
- else
- ret = unzOpenCurrentFilePassword( _unzFile, password );
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occurs"];
- success = NO;
- break;
- }
- // reading data and write to file
- int read ;
- unz_file_info fileInfo ={0};
- ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occurs while getting file info"];
- success = NO;
- unzCloseCurrentFile( _unzFile );
- break;
- }
- char* filename = (char*) malloc( fileInfo.size_filename +1 );
- unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
- filename[fileInfo.size_filename] = '\0';
-
- // check if it contains directory
- NSString * strPath = [NSString stringWithCString:filename encoding:self.stringEncoding];
- free( filename );
- if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
- {// contains a path
- strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
- }
-
- NSMutableData *fileMutableData = [NSMutableData data];
- do
- {
- read = unzReadCurrentFile(_unzFile, buffer, 4096);
- if (read >= 0)
- {
- if (read != 0)
- {
- [fileMutableData appendBytes:buffer length:read];
- }
- }
- else // if (read < 0)
- {
- ret = read; // result will be an error code
- success = NO;
- [self OutputErrorMessage:@"Failed to read zip file"];
- }
- } while (read > 0);
-
-
- if (fileMutableData.length > 0)
- {
- NSData *fileData = [NSData dataWithData:fileMutableData];
- [fileDictionary setObject:fileData forKey:strPath];
- }
-
- if (ret == UNZ_OK) {
- ret = unzCloseCurrentFile( _unzFile );
- if (ret != UNZ_OK) {
- [self OutputErrorMessage:@"file was unzipped but failed crc check"];
- success = NO;
- }
- }
-
- if (ret == UNZ_OK) {
- ret = unzGoToNextFile( _unzFile );
- }
-
- if (_progressBlock && _numFiles) {
- index++;
- int p = index*100/_numFiles;
- progress = p;
- _progressBlock(progress, index, _numFiles);
- }
- }
- } while (ret==UNZ_OK && ret!=UNZ_END_OF_LIST_OF_FILE);
-
- NSDictionary *resultDictionary = [NSDictionary dictionaryWithDictionary:fileDictionary];
- return resultDictionary;
- }
- /**
- * Close the zip file.
- *
- * @returns BOOL YES on success
- */
- -(BOOL) UnzipCloseFile
- {
- self.password = nil;
- if( _unzFile ) {
- int err = unzClose( _unzFile );
- _unzFile = nil;
- return err ==UNZ_OK;
- }
- return YES;
- }
- /**
- * Return a list of filenames that are in the zip archive.
- * No path information is available as this can be called before the zip is expanded.
- *
- * @returns NSArray list of filenames in the zip archive.
- */
- -(NSArray*) getZipFileContents // list the contents of the zip archive. must be called after UnzipOpenFile
- {
- int ret = unzGoToFirstFile( _unzFile );
- NSMutableArray * allFilenames = [NSMutableArray arrayWithCapacity:40];
-
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Failed"];
- }
-
- const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
-
- do{
- if( [_password length]==0 )
- ret = unzOpenCurrentFile( _unzFile );
- else
- ret = unzOpenCurrentFilePassword( _unzFile, password );
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occured"];
- break;
- }
-
- // reading data and write to file
- unz_file_info fileInfo ={0};
- ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
- if( ret!=UNZ_OK )
- {
- [self OutputErrorMessage:@"Error occurs while getting file info"];
- unzCloseCurrentFile( _unzFile );
- break;
- }
- char* filename = (char*) malloc( fileInfo.size_filename +1 );
- unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
- filename[fileInfo.size_filename] = '\0';
-
- // check if it contains directory
- NSString * strPath = [NSString stringWithCString:filename encoding:NSASCIIStringEncoding];
- free( filename );
- if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
- {// contains a path
- strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
- }
-
- // Copy name to array
- [allFilenames addObject:strPath];
-
- unzCloseCurrentFile( _unzFile );
- ret = unzGoToNextFile( _unzFile );
- } while( ret==UNZ_OK && UNZ_OK!=UNZ_END_OF_LIST_OF_FILE );
-
- // return an immutable array.
- return [NSArray arrayWithArray:allFilenames];
- }
- #pragma mark wrapper for delegate
- /**
- * send the ErrorMessage: to the delegate if it responds to it.
- */
- -(void) OutputErrorMessage:(NSString*) msg
- {
- if( _delegate && [_delegate respondsToSelector:@selector(ErrorMessage:)] )
- [_delegate ErrorMessage:msg];
- }
- /**
- * send the OverWriteOperation: selector to the delegate if it responds to it,
- * returning the result, or YES by default.
- */
- -(BOOL) OverWrite:(NSString*) file
- {
- if( _delegate && [_delegate respondsToSelector:@selector(OverWriteOperation:)] )
- return [_delegate OverWriteOperation:file];
- return YES;
- }
- #pragma mark get NSDate object for 1980-01-01
- -(NSDate*) Date1980
- {
- NSDateComponents *comps = [[NSDateComponents alloc] init];
- [comps setDay:1];
- [comps setMonth:1];
- [comps setYear:1980];
- NSCalendar *gregorian = [[NSCalendar alloc]
- initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSDate *date = [gregorian dateFromComponents:comps];
-
- [comps release];
- [gregorian release];
- return date;
- }
- @end
- @implementation NSFileManager(ZipArchive)
- - (NSDictionary *)_attributesOfItemAtPath:(NSString *)path followingSymLinks:(BOOL)followingSymLinks error:(NSError **)error
- {
- // call file manager default action, which is to not follow symlinks
- NSDictionary* results = [self attributesOfItemAtPath:path error:error];
- if (followingSymLinks && results && (error ? *error == nil : YES)) {
- if ([[results fileType] isEqualToString:NSFileTypeSymbolicLink]) {
- // follow the symlink
- NSString* realPath = [self destinationOfSymbolicLinkAtPath:path error:error];
- if (realPath && (error ? *error == nil : YES)) {
- return [self _attributesOfItemAtPath:realPath followingSymLinks:followingSymLinks error:error];
- } else {
- // failure to resolve symlink should be an error returning nil and error will already be set.
- return nil;
- }
- }
- }
- return results;
- }
- @end
|