diff --git a/SSZipArchive.m b/SSZipArchive.m index 8665ca1..a9f9a46 100644 --- a/SSZipArchive.m +++ b/SSZipArchive.m @@ -106,27 +106,35 @@ archivePath:path fileInfo:fileInfo]; } - struct ZipExtraField - { - short headerId; - short dataSize; - }; - - struct ZipExtraField extra = { 0 }; - char *filename = (char *)malloc(fileInfo.size_filename + 1); - unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, &extra, sizeof(struct ZipExtraField), NULL, 0); + unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0); filename[fileInfo.size_filename] = '\0'; - - NSLog(@"Filename: %s", filename); - - //extra.data = (char *)malloc(fileInfo.size_file_extra); - //unzGetLocalExtrafield(zip, &extra, sizeof(struct ZipExtraField)); - NSLog(@"Header ID: %ul", fileInfo.size_file_extra); + // + // NOTE + // I used the ZIP spec from here: + // http://www.pkware.com/documents/casestudies/APPNOTE.TXT + // + // ...to deduce this method of detecting whether the file in the ZIP is a symbolic link. + // If it is, it is listed as a directory but has an uncompressed data size greater than + // zero (real directories have it equal to 0) and the included, uncompressed data is the + // symbolic link path. + // - //NSLog(@"Data: %s", extra.data); - + const uLong ZipDirectoryVersion = 10; + const uLong ZipCompressionMethodStore = 0; + + BOOL fileIsSymbolicLink = NO; + + if((fileInfo.version_needed == ZipDirectoryVersion) && // Is it a directory? + (fileInfo.compression_method == ZipCompressionMethodStore) && // Is it compressed? + (fileInfo.compressed_size > 0)) // Is there any data there? + { + fileIsSymbolicLink = YES; + } + + //NSLog(@"\"%s\" is symbolic link? %@", filename, fileIsSymbolicLink ? @"Yes." : @"No."); + // Check if it contains directory NSString *strPath = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; BOOL isDirectory = NO; @@ -154,7 +162,8 @@ NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription); } - [directoriesModificationDates addObject: [NSDictionary dictionaryWithObjectsAndKeys:fullPath, @"path", modDate, @"modDate", nil]]; + if(!fileIsSymbolicLink) + [directoriesModificationDates addObject: [NSDictionary dictionaryWithObjectsAndKeys:fullPath, @"path", modDate, @"modDate", nil]]; if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) { unzCloseCurrentFile(zip); @@ -162,35 +171,63 @@ continue; } - NSLog(@"External file attributes: %lu", fileInfo.external_fa); - - FILE *fp = fopen((const char*)[fullPath UTF8String], "wb"); - while (fp) { - int readBytes = unzReadCurrentFile(zip, buffer, 4096); + if(!fileIsSymbolicLink) + { + FILE *fp = fopen((const char*)[fullPath UTF8String], "wb"); + while (fp) { + int readBytes = unzReadCurrentFile(zip, buffer, 4096); - if (readBytes > 0) { - fwrite(buffer, readBytes, 1, fp ); - } else { - break; - } - } - - if (fp) { - fclose(fp); - - // Set the original datetime property - if (fileInfo.dosDate != 0) { - NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate]; - NSDictionary *attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate]; - - if (attr) { - if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) { - // Can't set attributes - NSLog(@"[SSZipArchive] Failed to set attributes"); - } - } - } - } + if (readBytes > 0) { + fwrite(buffer, readBytes, 1, fp ); + } else { + break; + } + } + + if (fp) { + fclose(fp); + + // Set the original datetime property + if (fileInfo.dosDate != 0) { + NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate]; + NSDictionary *attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate]; + + if (attr) { + if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) { + // Can't set attributes + NSLog(@"[SSZipArchive] Failed to set attributes"); + } + } + } + } + } + else + { + // Get the path for the symbolic link + + NSURL* symlinkURL = [NSURL fileURLWithPath:fullPath]; + NSMutableString* destinationPath = [NSMutableString string]; + + int bytesRead = 0; + while((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0) + { + buffer[bytesRead] = 0; + [destinationPath appendString:[NSString stringWithUTF8String:(const char*)buffer]]; + } + + //NSLog(@"Symlinking to: %@", destinationPath); + + NSURL* destinationURL = [NSURL fileURLWithPath:destinationPath]; + + // Create the symbolic link + NSError* symlinkError = nil; + [fileManager createSymbolicLinkAtURL:symlinkURL withDestinationURL:destinationURL error:&symlinkError]; + + if(symlinkError != nil) + { + NSLog(@"Failed to create symbolic link at \"%@\" to \"%@\". Error: %@", symlinkURL.absoluteString, destinationURL.absoluteString, symlinkError.localizedDescription); + } + } unzCloseCurrentFile( zip ); ret = unzGoToNextFile( zip ); diff --git a/Tests/SSZipArchive.xcodeproj/project.pbxproj b/Tests/SSZipArchive.xcodeproj/project.pbxproj index b22e991..d8c946b 100644 --- a/Tests/SSZipArchive.xcodeproj/project.pbxproj +++ b/Tests/SSZipArchive.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */ = {isa = PBXBuildFile; fileRef = B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */; }; C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */ = {isa = PBXBuildFile; fileRef = C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */; }; C5AE4E6D155A8B010045F3ED /* SymbolicLink.zip in Resources */ = {isa = PBXBuildFile; fileRef = C5AE4E6C155A8B010045F3ED /* SymbolicLink.zip */; }; - C5AE4E70155A96CF0045F3ED /* ZipTest.zip in Resources */ = {isa = PBXBuildFile; fileRef = C5AE4E6F155A96CF0045F3ED /* ZipTest.zip */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -45,7 +44,6 @@ B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = TestPasswordArchive.zip; sourceTree = ""; }; C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = IncorrectHeaders.zip; sourceTree = ""; }; C5AE4E6C155A8B010045F3ED /* SymbolicLink.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = SymbolicLink.zip; sourceTree = ""; }; - C5AE4E6F155A96CF0045F3ED /* ZipTest.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = ZipTest.zip; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -120,7 +118,6 @@ B215FB5E143AD505003AC546 /* SSZipArchiveTests */ = { isa = PBXGroup; children = ( - C5AE4E6F155A96CF0045F3ED /* ZipTest.zip */, B215FB61143AD514003AC546 /* SSZipArchiveTests.m */, B215FB5F143AD514003AC546 /* SSZipArchiveTests-Info.plist */, C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */, @@ -187,7 +184,6 @@ B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */, C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */, C5AE4E6D155A8B010045F3ED /* SymbolicLink.zip in Resources */, - C5AE4E70155A96CF0045F3ED /* ZipTest.zip in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/SSZipArchiveTests.m b/Tests/SSZipArchiveTests.m index 6e8db86..8bf92e9 100644 --- a/Tests/SSZipArchiveTests.m +++ b/Tests/SSZipArchiveTests.m @@ -86,12 +86,17 @@ - (void)testUnzippingWithSymlinkedFileInside { - NSString* zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"ZipTest" ofType:@"zip"]; + NSString* zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"SymbolicLink" ofType:@"zip"]; NSString* outputPath = [self _cachesPath:@"SymbolicLink"]; [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:self]; - BOOL symbolicLinkPersists = NO; + NSString* testSymlink = [outputPath stringByAppendingPathComponent:@"SymbolicLink/GitHub.app"]; + + NSError* error = nil; + NSString* symlinkPath = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:testSymlink error:&error]; + + bool symbolicLinkPersists = ([symlinkPath isEqualToString:@"/Applications/GitHub.app"]) && (error == nil); STAssertTrue(symbolicLinkPersists, @"Symbolic links should persist from the original archive to the outputted files."); } diff --git a/Tests/SymbolicLink.zip b/Tests/SymbolicLink.zip index bcb96a7..952827d 100644 Binary files a/Tests/SymbolicLink.zip and b/Tests/SymbolicLink.zip differ diff --git a/Tests/ZipTest.zip b/Tests/ZipTest.zip deleted file mode 100644 index 2d30e40..0000000 Binary files a/Tests/ZipTest.zip and /dev/null differ