|
@@ -0,0 +1,691 @@
|
|
|
+/**
|
|
|
+// 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
|
|
|
+
|