Cog/Frameworks/Sparkle/Sparkle/SUDiskImageUnarchiver.m
2014-08-01 03:53:07 -07:00

168 lines
5 KiB
Objective-C

//
// 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"
#import "SULog.h"
#include <CoreServices/CoreServices.h>
@implementation SUDiskImageUnarchiver
+ (BOOL)canUnarchivePath:(NSString *)path
{
return [[path pathExtension] isEqualToString:@"dmg"];
}
// Called on a non-main thread.
- (void)extractDMG
{
@autoreleasepool {
[self extractDMGWithPassword:nil];
}
}
// Called on a non-main thread.
- (void)extractDMGWithPassword:(NSString *)__unused password
{
@autoreleasepool {
BOOL mountedSuccessfully = NO;
SULog(@"Extracting %@ as a DMG", self.archivePath);
// get a unique mount point path
NSString *mountPoint = nil;
FSRef tmpRef;
NSFileManager *manager;
NSError *error;
NSArray *contents;
// We have to declare these before a goto to prevent an error under ARC.
// No, we cannot have them in the dispatch_async calls, as the goto "jump enters
// lifetime of block which strongly captures a variable"
dispatch_block_t delegateFailure = ^{
[self notifyDelegateOfFailure];
};
dispatch_block_t delegateSuccess = ^{
[self notifyDelegateOfSuccess];
};
do
{
// Using NSUUID would make creating UUIDs be done in Cocoa,
// and thus managed under ARC. Sadly, the class is in 10.8 and later.
CFUUIDRef uuid = CFUUIDCreate(NULL);
if (uuid)
{
NSString *uuidString = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
if (uuidString)
{
mountPoint = [@"/Volumes" stringByAppendingPathComponent:uuidString];
}
CFRelease(uuid);
}
}
while (noErr == FSPathMakeRefWithOptions((UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL));
NSData *promptData = nil;
promptData = [NSData dataWithBytes:"yes\n" length:4];
NSArray *arguments = @[@"attach", self.archivePath, @"-mountpoint", mountPoint, /*@"-noverify",*/ @"-nobrowse", @"-noautoopen"];
NSData *output = nil;
NSInteger taskResult = -1;
@try
{
NTSynchronousTask *task = [[NTSynchronousTask alloc] init];
[task run:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:promptData];
taskResult = [task result];
output = [[task output] copy];
}
@catch (NSException *)
{
goto reportError;
}
if (taskResult != 0)
{
NSString *resultStr = output ? [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] : nil;
SULog(@"hdiutil failed with code: %ld data: <<%@>>", (long)taskResult, resultStr);
goto reportError;
}
mountedSuccessfully = YES;
// Now that we've mounted it, we need to copy out its contents.
manager = [[NSFileManager alloc] init];
contents = [manager contentsOfDirectoryAtPath:mountPoint error:&error];
if (error)
{
SULog(@"Couldn't enumerate contents of archive mounted at %@: %@", mountPoint, error);
goto reportError;
}
for (NSString *item in contents)
{
NSString *fromPath = [mountPoint stringByAppendingPathComponent:item];
NSString *toPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:item];
// We skip any files in the DMG which are not readable.
if (![manager isReadableFileAtPath:fromPath]) {
continue;
}
SULog(@"copyItemAtPath:%@ toPath:%@", fromPath, toPath);
if (![manager copyItemAtPath:fromPath toPath:toPath error:&error])
{
SULog(@"Couldn't copy item: %@ : %@", error, error.userInfo ? error.userInfo : @"");
goto reportError;
}
}
dispatch_async(dispatch_get_main_queue(), delegateSuccess);
goto finally;
reportError:
dispatch_async(dispatch_get_main_queue(), delegateFailure);
finally:
if (mountedSuccessfully)
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:@[@"detach", mountPoint, @"-force"]];
else
SULog(@"Can't mount DMG %@", self.archivePath);
}
}
- (void)start
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self extractDMG];
});
}
+ (void)load
{
[self registerImplementation:self];
}
- (BOOL)isEncrypted:(NSData *)resultData
{
BOOL result = NO;
if(resultData)
{
NSString *data = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
if ((data != nil) && !NSEqualRanges([data rangeOfString:@"passphrase-count"], NSMakeRange(NSNotFound, 0)))
{
result = YES;
}
}
return result;
}
@end