Permalink
Browse files

Fixes 236695

Refactored Sparkle's unarchiving system into SUUnarchiver, a factory for SUPipedUnarchiver and SUDiskImageUnarchiver. I removed that nasty cleanUp call by now copying out the contents of the DMG into the /tmp directory and unmounting. Nice!

This changed a fair amount so please test with your build and let me know if it explodes things. Works in my tests, though.
  • Loading branch information...
1 parent 06bda29 commit 93ea93dd389a2ded97f2a38a62643e956197cccc @andymatuschak andymatuschak committed Jun 19, 2008
@@ -62,7 +62,6 @@ - (void)applicationWillTerminate:(NSNotification *)note
- (void)installerFinishedForHostBundle:(NSBundle *)hb
{
if (hb != hostBundle) { return; }
- [unarchiver cleanUp];
if (!postponingInstallation)
[self relaunchHostApp];
}
View
@@ -162,9 +162,10 @@ - (void)extractUpdate
}
}
- unarchiver = [[SUUnarchiver alloc] init];
+ unarchiver = [[SUUnarchiver unarchiverForURL:[[[NSURL alloc] initFileURLWithPath:downloadPath] autorelease]] retain];
+#warning check for nonexistant unarchiver
[unarchiver setDelegate:self];
- [unarchiver unarchivePath:downloadPath];
+ [unarchiver start];
}
- (void)unarchiverDidFinish:(SUUnarchiver *)ua
@@ -197,7 +198,6 @@ - (void)installUpdate
- (void)installerFinishedForHostBundle:(NSBundle *)hb
{
if (hb != hostBundle) { return; }
- [unarchiver cleanUp];
[self relaunchHostApp];
}
View
@@ -0,0 +1,20 @@
+//
+// SUDiskImageUnarchiver.h
+// Sparkle
+//
+// Created by Andy Matuschak on 6/16/08.
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#ifndef SUDISKIMAGEUNARCHIVER_H
+#define SUDISKIMAGEUNARCHIVER_H
+
+#import <Cocoa/Cocoa.h>
+#import "SUUnarchiver.h"
+
+@interface SUDiskImageUnarchiver : SUUnarchiver {
+}
+
+@end
+
+#endif
View
@@ -0,0 +1,79 @@
+//
+// SUDiskImageUnarchiver.m
+// Sparkle
+//
+// Created by Andy Matuschak on 6/16/08.
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#import "SUDiskImageUnarchiver.h"
+#import "SUUnarchiver_Private.h"
+#import "NTSynchronousTask.h"
+
+@implementation SUDiskImageUnarchiver
+
++ (BOOL)_canUnarchiveURL:(NSURL *)URL
+{
+ return [URL conformsToType:@"public.disk-image"];
+}
+
+- (void)start
+{
+ [NSThread detachNewThreadSelector:@selector(_extractDMG) toTarget:self withObject:nil];
+}
+
+- (void)_extractDMG
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSString *archivePath = [archiveURL path];
+ BOOL mountedSuccessfully = NO;
+
+ // get a unique mount point path
+ NSString *mountPrefix = [@"/Volumes" stringByAppendingPathComponent:[[archivePath lastPathComponent] stringByDeletingPathExtension]];
+ NSString *mountPoint = [mountPrefix stringByAppendingString:[[NSProcessInfo processInfo] globallyUniqueString]];
+
+ if ([[NSFileManager defaultManager] fileExistsAtPath:mountPoint]) goto reportError;
+
+ // create mount point folder
+ [[NSFileManager defaultManager] createDirectoryAtPath:mountPoint attributes:nil];
+ if (![[NSFileManager defaultManager] fileExistsAtPath:mountPoint]) goto reportError;
+
+ NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, @"-noverify", @"-nobrowse", @"-noautoopen", nil];
+ // set up a pipe and push "yes" (y works too), this will accept any license agreement crap
+ // not every .dmg needs this, but this will make sure it works with everyone
+ NSData* yesData = [[[NSData alloc] initWithBytes:"yes\n" length:4] autorelease];
+
+ NSData *result = [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData];
+ if (!result) goto reportError;
+ mountedSuccessfully = YES;
+
+ // Now that we've mounted it, we need to copy out its contents.
+ NSString *targetPath = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[mountPoint lastPathComponent]];
+ if (![[NSFileManager defaultManager] createDirectoryAtPath:targetPath attributes:nil]) goto reportError;
+
+ // We can't just copyPath: from the volume root because that always fails. Seems to be a bug.
+ id subpathEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath:mountPoint] objectEnumerator], currentSubpath;
+ while ((currentSubpath = [subpathEnumerator nextObject]))
+ {
+ if (![[NSFileManager defaultManager] copyPath:[mountPoint stringByAppendingPathComponent:currentSubpath] toPath:[targetPath stringByAppendingPathComponent:currentSubpath] handler:nil])
+ goto reportError;
+ }
+
+ [self performSelectorOnMainThread:@selector(_notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
+ goto finally;
+
+reportError:
+ [self performSelectorOnMainThread:@selector(_notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
+
+finally:
+ if (mountedSuccessfully)
+ [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", mountPoint, @"-force", nil]];
+ [pool drain];
+}
+
++ (void)load
+{
+ [self _registerImplementation:self];
+}
+
+@end
View
@@ -0,0 +1,21 @@
+//
+// SUPipedUnarchiver.h
+// Sparkle
+//
+// Created by Andy Matuschak on 6/16/08.
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#ifndef SUPIPEDUNARCHIVER_H
+#define SUPIPEDUNARCHIVER_H
+
+#import <Cocoa/Cocoa.h>
+#import "SUUnarchiver.h"
+
+@interface SUPipedUnarchiver : SUUnarchiver {
+
+}
+
+@end
+
+#endif
View
@@ -0,0 +1,111 @@
+//
+// SUPipedUnarchiver.m
+// Sparkle
+//
+// Created by Andy Matuschak on 6/16/08.
+// Copyright 2008 Andy Matuschak. All rights reserved.
+//
+
+#import "SUPipedUnarchiver.h"
+#import "SUUnarchiver_Private.h"
+
+@implementation SUPipedUnarchiver
+
++ (SEL)_selectorConformingToTypeOfURL:(NSURL *)URL
+{
+ static NSDictionary *typeSelectorDictionary;
+ if (!typeSelectorDictionary)
+ typeSelectorDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:@"_extractZIP", @"public.zip-archive", @"_extractTGZ", @"org.gnu.gnu-zip-tar-archive", @"_extractTBZ", @"org.bzip.bzip2-tar-archive", @"_extractTAR", @"public.tar-archive", nil] retain];
+
+ NSEnumerator *typeEnumerator = [typeSelectorDictionary keyEnumerator];
+ id currentType;
+ while ((currentType = [typeEnumerator nextObject]))
+ {
+ if ([URL conformsToType:currentType])
+ return NSSelectorFromString([typeSelectorDictionary objectForKey:currentType]);
+ }
+ return NULL;
+}
+
+- (void)start
+{
+ [NSThread detachNewThreadSelector:[[self class] _selectorConformingToTypeOfURL:archiveURL] toTarget:self withObject:nil];
+}
+
++ (BOOL)_canUnarchiveURL:(NSURL *)URL
+{
+ return ([self _selectorConformingToTypeOfURL:URL] != nil);
+}
+
+// This method abstracts the types that use a command line tool piping data from stdin.
+- (void)_extractArchivePipingDataToCommand:(NSString *)command
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSString *archivePath = [archiveURL path];
+
+ // Get the file size.
+ NSNumber *fs = [[[NSFileManager defaultManager] fileAttributesAtPath:archivePath traverseLink:NO] objectForKey:NSFileSize];
+ if (fs == nil) goto reportError;
+
+ // Thank you, Allan Odgaard!
+ // (who wrote the following extraction alg.)
+
+ long current = 0;
+ FILE *fp, *cmdFP;
+ if ((fp = fopen([archivePath fileSystemRepresentation], "r")))
+ {
+ setenv("DESTINATION", [[archivePath stringByDeletingLastPathComponent] UTF8String], 1);
+ if ((cmdFP = popen([command fileSystemRepresentation], "w")))
+ {
+ char buf[32*1024];
+ long len;
+ while((len = fread(buf, 1, 32*1024, fp)))
+ {
+ current += len;
+ fwrite(buf, 1, len, cmdFP);
+ [self performSelectorOnMainThread:@selector(_notifyDelegateOfExtractedLength:) withObject:[NSNumber numberWithLong:len] waitUntilDone:NO];
+ }
+ pclose(cmdFP);
+ }
+ else goto reportError;
+ }
+ else goto reportError;
+
+ [self performSelectorOnMainThread:@selector(_notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
+ goto finally;
+
+reportError:
+ [self performSelectorOnMainThread:@selector(_notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
+
+finally:
+ if (fp)
+ fclose(fp);
+ [pool drain];
+}
+
+- (void)_extractTAR
+{
+ return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""];
+}
+
+- (void)_extractTGZ
+{
+ return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""];
+}
+
+- (void)_extractTBZ
+{
+ return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""];
+}
+
+- (void)_extractZIP
+{
+ return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""];
+}
+
++ (void)load
+{
+ [self _registerImplementation:self];
+}
+
+@end
View
@@ -11,12 +11,13 @@
@interface SUUnarchiver : NSObject {
id delegate;
- NSString *archivePath;
+ NSURL *archiveURL;
}
-- (void)unarchivePath:(NSString *)path;
++ (SUUnarchiver *)unarchiverForURL:(NSURL *)URL;
- (void)setDelegate:delegate;
-- (void)cleanUp;
+
+- (void)start;
@end
Oops, something went wrong.

0 comments on commit 93ea93d

Please sign in to comment.