Changeset 147
- Timestamp:
- 04/15/08 00:22:18 (1 month ago)
- Files:
-
- trunk/NSFileManager+Authentication.h (modified) (1 diff)
- trunk/NSFileManager+Authentication.m (modified) (7 diffs)
- trunk/NTSynchronousTask.h (modified) (1 diff)
- trunk/NTSynchronousTask.m (modified) (1 diff)
- trunk/SUConstants.h (modified) (1 diff)
- trunk/SUConstants.m (modified) (1 diff)
- trunk/SUInstaller.h (added)
- trunk/SUInstaller.m (added)
- trunk/SUPackageInstaller.h (added)
- trunk/SUPackageInstaller.m (added)
- trunk/SUPlainInstaller.h (added)
- trunk/SUPlainInstaller.m (added)
- trunk/SUStandardVersionComparator.m (modified) (1 diff)
- trunk/SUStatusController.m (modified) (1 diff)
- trunk/SUSystemProfiler.h (modified) (1 diff)
- trunk/SUUnarchiver.h (modified) (1 diff)
- trunk/SUUnarchiver.m (modified) (7 diffs)
- trunk/SUUpdater.h (modified) (2 diffs)
- trunk/SUUpdater.m (modified) (5 diffs)
- trunk/Sparkle.h (modified) (1 diff)
- trunk/Sparkle.xcodeproj/project.pbxproj (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/NSFileManager+Authentication.h
r99 r147 11 11 12 12 @interface NSFileManager (SUAuthenticationAdditions) 13 - (BOOL)copyPath :(NSString *)src overPath:(NSString *)dst withAuthentication:(BOOL)useAuthentication;13 - (BOOL)copyPathWithAuthentication:(NSString *)src overPath:(NSString *)dst error:(NSError **)error; 14 14 @end 15 15 trunk/NSFileManager+Authentication.m
r145 r147 19 19 20 20 #import "NSFileManager+ExtendedAttributes.h" 21 22 // TN OV02 says that the range between 1000 and 9999 can be used for 23 // application-defined errors. errToolFailedError will indicate that an 24 // executed program crashed or indicated failure with a nonzero exit status 25 static const OSStatus errToolFailedError = 1000; 26 27 static OSStatus AuthorizationExecuteWithPrivilegesAndWait( 28 AuthorizationRef authorization, 29 const char* executablePath, 30 AuthorizationFlags options, 31 const char* const* arguments) { 21 static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authorization, const char* executablePath, AuthorizationFlags options, const char* const* arguments) 22 { 32 23 sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL); 33 OSStatus ret; 34 ret = AuthorizationExecuteWithPrivileges(authorization, 35 executablePath, 36 options, 37 (char* const*)arguments, 38 NULL); 39 if (ret == errAuthorizationSuccess) { 24 BOOL returnValue = YES; 25 26 if (AuthorizationExecuteWithPrivileges(authorization, executablePath, options, (char* const*)arguments, NULL) == errAuthorizationSuccess) 27 { 40 28 int status; 41 29 pid_t pid = wait(&status); 42 if (pid == -1 || 43 !WIFEXITED(status) || WEXITSTATUS(status) != 0) { 44 ret = errToolFailedError; 45 } 46 } 30 if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 31 returnValue = NO; 32 } 33 else 34 returnValue = NO; 35 47 36 signal(SIGCHLD, oldSigChildHandler); 48 return ret ;37 return returnValue; 49 38 } 50 39 … … 86 75 } 87 76 88 - (NSString *)_temporaryCopyNameForPath:(NSString *)filename 89 { 90 return [[[filename stringByDeletingPathExtension] stringByAppendingString:@".old"] stringByAppendingPathExtension:[filename pathExtension]]; 91 } 92 93 - (BOOL)_copyPathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst 77 - (NSString *)_temporaryCopyNameForPath:(NSString *)path 78 { 79 // Let's try to read the version number so the filename will be more meaningful. 80 NSString *postFix; 81 NSBundle *bundle; 82 if ((bundle = [NSBundle bundleWithPath:path])) 83 { 84 // We'll clean it up a little for safety. 85 NSMutableCharacterSet *validCharacters = [NSMutableCharacterSet alphanumericCharacterSet]; 86 [validCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-()"]]; 87 postFix = [[bundle objectForInfoDictionaryKey:@"CFBundleVersion"] stringByTrimmingCharactersInSet:[validCharacters invertedSet]]; 88 } 89 else 90 postFix = @"old"; 91 return [[[path stringByDeletingPathExtension] stringByAppendingFormat:@" (%@)", postFix] stringByAppendingPathExtension:[path pathExtension]]; 92 } 93 94 - (BOOL)_copyPathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst error:(NSError **)error 94 95 { 95 96 NSString *tmp = [self _temporaryCopyNameForPath:dst]; … … 98 99 const char* dstPath = [dst fileSystemRepresentation]; 99 100 100 struct stat sb, dstSB; 101 if ((stat(srcPath, &sb) != 0) || 102 (stat(tmpPath, &sb) == 0) || 103 (stat(dstPath, &dstSB) != 0)) { 104 return NO; 105 } 101 struct stat dstSB; 102 stat(dstPath, &dstSB); 106 103 107 104 AuthorizationRef auth = NULL; … … 123 120 124 121 const char* executables[] = { 122 "/bin/rm", 125 123 "/bin/mv", 126 124 "/bin/mv", … … 136 134 // list. 137 135 const char* const argumentLists[][4] = { 136 { "-rf", tmpPath, NULL }, // make room for the temporary file... this is kinda unsafe; should probably do something better. 138 137 { "-f", dstPath, tmpPath, NULL }, // mv 139 138 { "-f", srcPath, dstPath, NULL }, // mv … … 147 146 int commandIndex = 0; 148 147 for (; executables[commandIndex] != NULL; ++commandIndex) { 149 if (res) { 150 res = ( 151 AuthorizationExecuteWithPrivilegesAndWait(auth, 152 executables[commandIndex], 153 kAuthorizationFlagDefaults, 154 argumentLists[commandIndex]) == 155 errAuthorizationSuccess); 156 } 148 if (res) 149 res = AuthorizationExecuteWithPrivilegesAndWait(auth, executables[commandIndex], kAuthorizationFlagDefaults, argumentLists[commandIndex]); 157 150 } 158 151 … … 178 171 179 172 for (; executables[commandIndex] != NULL; ++commandIndex) { 180 if (res) { 181 res = ( 182 AuthorizationExecuteWithPrivilegesAndWait(auth, 183 executables[commandIndex], 184 kAuthorizationFlagDefaults, 185 argumentLists[commandIndex]) == 186 errAuthorizationSuccess); 187 } 173 if (res) 174 res = AuthorizationExecuteWithPrivilegesAndWait(auth, executables[commandIndex], kAuthorizationFlagDefaults, argumentLists[commandIndex]); 188 175 } 189 176 190 177 AuthorizationFree(auth, 0); 178 179 if (!res) 180 { 181 // Something went wrong somewhere along the way, but we're not sure exactly where. 182 NSString *errorMessage = [NSString stringWithFormat:@"Authenticated file copy from %@ to %@ failed.", src, dst]; 183 if (error != NULL) 184 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]]; 185 } 186 } 187 else 188 { 189 if (error != NULL) 190 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:[NSDictionary dictionaryWithObject:@"Couldn't get permission to authenticate." forKey:NSLocalizedDescriptionKey]]; 191 191 } 192 192 return res; 193 193 } 194 194 195 - (BOOL)copyPath:(NSString *)src overPath:(NSString *)dst withAuthentication:(BOOL)useAuthentication 196 { 197 if ([[NSFileManager defaultManager] isWritableFileAtPath:dst] && [[NSFileManager defaultManager] isWritableFileAtPath:[dst stringByDeletingLastPathComponent]]) 198 { 199 NSInteger tag = 0; 200 BOOL result; 201 NSString *tmpPath = [self _temporaryCopyNameForPath:dst]; 202 result = [[NSFileManager defaultManager] movePath:dst toPath:tmpPath handler:self]; 203 result &= [[NSFileManager defaultManager] copyPath:src toPath:dst handler:nil]; 204 result &= [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[tmpPath stringByDeletingLastPathComponent] destination:@"" files:[NSArray arrayWithObject:[tmpPath lastPathComponent]] tag:&tag]; 205 206 // If the currently-running application is trusted, the new 207 // version should be trusted as well. Remove it from the 208 // quarantine to avoid a delay at launch, and to avoid 209 // presenting the user with a confusing trust dialog. 210 // 211 // This needs to be done after the application is moved to its 212 // new home in case it's moved across filesystems: if that 213 // happens, the move is actually a copy, and it may result 214 // in the application being quarantined. 215 if (result) 216 [self releaseFromQuarantine:dst]; 217 218 return result; 219 } 220 else if (useAuthentication == YES) 221 return [self _copyPathWithForcedAuthentication:src toPath:dst]; 222 else 195 - (BOOL)copyPathWithAuthentication:(NSString *)src overPath:(NSString *)dst error:(NSError **)error 196 { 197 if (![[NSFileManager defaultManager] fileExistsAtPath:dst]) 198 { 199 NSString *errorMessage = [NSString stringWithFormat:@"Couldn't copy %@ over %@ because there is no file at %@.", src, dst, dst]; 200 if (error != NULL) 201 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]]; 223 202 return NO; 224 } 225 226 - (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 227 { 228 NSLog(@"Sparkle: An error occurred in copying %@ to %@: %@", srcPath, dstPath, [error localizedDescription]); 229 return NO; 230 } 231 232 - (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error movingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath 233 { 234 NSLog(@"Sparkle: An error occurred in moving %@ to %@: %@", srcPath, dstPath, [error localizedDescription]); 235 return NO; 203 } 204 205 if (![[NSFileManager defaultManager] isWritableFileAtPath:dst] || ![[NSFileManager defaultManager] isWritableFileAtPath:[dst stringByDeletingLastPathComponent]]) 206 return [self _copyPathWithForcedAuthentication:src toPath:dst error:error]; 207 208 NSString *tmpPath = [self _temporaryCopyNameForPath:dst]; 209 210 // We get more error information if we're running on Leopard, so let's use that if we can. 211 if ([[NSFileManager defaultManager] respondsToSelector:@selector(moveItemAtPath:toPath:error:)]) 212 { 213 if (![[NSFileManager defaultManager] moveItemAtPath:dst toPath:tmpPath error:error]) { return NO; } 214 if (![[NSFileManager defaultManager] copyItemAtPath:src toPath:dst error:error]) { return NO; } 215 } 216 else // We just get generic error messages. 217 { 218 if (![[NSFileManager defaultManager] movePath:dst toPath:tmpPath handler:self]) 219 { 220 if (error != NULL) 221 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Couldn't move %@ to %@.", dst, tmpPath] forKey:NSLocalizedDescriptionKey]]; 222 return NO; 223 } 224 if (![[NSFileManager defaultManager] copyPath:src toPath:dst handler:self]) 225 { 226 if (error != NULL) 227 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Couldn't copy %@ to %@.", src, dst] forKey:NSLocalizedDescriptionKey]]; 228 return NO; 229 } 230 } 231 232 // Trash the old copy of the app. 233 NSInteger tag = 0; 234 if (![[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[tmpPath stringByDeletingLastPathComponent] destination:@"" files:[NSArray arrayWithObject:[tmpPath lastPathComponent]] tag:&tag]) 235 { 236 if (error != NULL) 237 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Couldn't move %@ to the trash. This is often a sign of a permissions error.", src, dst] forKey:NSLocalizedDescriptionKey]]; 238 return NO; 239 } 240 241 // If the currently-running application is trusted, the new 242 // version should be trusted as well. Remove it from the 243 // quarantine to avoid a delay at launch, and to avoid 244 // presenting the user with a confusing trust dialog. 245 // 246 // This needs to be done after the application is moved to its 247 // new home in case it's moved across filesystems: if that 248 // happens, the move is actually a copy, and it may result 249 // in the application being quarantined. 250 [self releaseFromQuarantine:dst]; 251 252 return YES; 236 253 } 237 254 trunk/NTSynchronousTask.h
r99 r147 4 4 // 5 5 // Created by Steve Gehrman on 9/29/05. 6 // Copyright 2005 __MyCompanyName__. All rights reserved.6 // Copyright 2005 Steve Gehrman. All rights reserved. 7 7 // 8 8 trunk/NTSynchronousTask.m
r105 r147 4 4 // 5 5 // Created by Steve Gehrman on 9/29/05. 6 // Copyright 2005 __MyCompanyName__. All rights reserved.6 // Copyright 2005 Steve Gehrman. All rights reserved. 7 7 // 8 8 trunk/SUConstants.h
r127 r147 28 28 extern NSString *SUSendProfileInfoKey; 29 29 30 extern NSString *SUSparkleErrorDomain; 31 extern OSStatus SUFileCopyFailure; 32 extern OSStatus SUAuthenticationFailure; 33 extern OSStatus SUMissingUpdateError; 34 extern OSStatus SUMissingInstallerToolError; 35 30 36 // NSInteger is a type that was added to Leopard. 31 37 // Here is some glue to ensure that NSInteger will work with pre-10.5 SDKs: trunk/SUConstants.m
r127 r147 25 25 NSString *SUEnableAutomaticChecksKey = @"SUEnableAutomaticChecks"; 26 26 NSString *SUSendProfileInfoKey = @"SUSendProfileInfo"; 27 28 NSString *SUSparkleErrorDomain = @"SUSparkleErrorDomain"; 29 OSStatus SUFileCopyFailure = 9000; 30 OSStatus SUAuthenticationFailure = 9001; 31 OSStatus SUMissingUpdateError = 9002; 32 OSStatus SUMissingInstallerToolError = 9003; trunk/SUStandardVersionComparator.m
r132 r147 4 4 // 5 5 // Created by Andy Matuschak on 12/21/07. 6 // Copyright 2007 __MyCompanyName__. All rights reserved.6 // Copyright 2007 Andy Matuschak. All rights reserved. 7 7 // 8 8 trunk/SUStatusController.m
r136 r147 103 103 maxProgressValue = value; 104 104 [self setProgressValue:0]; 105 [progressBar setIndeterminate:(value == 0)]; 105 106 } 106 107 trunk/SUSystemProfiler.h
r114 r147 4 4 // 5 5 // Created by Andy Matuschak on 12/22/07. 6 // Copyright 2007 __MyCompanyName__. All rights reserved.6 // Copyright 2007 Andy Matuschak. All rights reserved. 7 7 // 8 8 trunk/SUUnarchiver.h
r99 r147 12 12 @interface SUUnarchiver : NSObject { 13 13 id delegate; 14 NSString *archivePath; 14 15 } 15 16 16 17 - (void)unarchivePath:(NSString *)path; 17 18 - (void)setDelegate:delegate; 19 - (void)cleanUp; 18 20 19 21 @end trunk/SUUnarchiver.m
r96 r147 14 14 15 15 // This method abstracts the types that use a command line tool piping data from stdin. 16 - (BOOL)_extractArchiveP ath:archivePath pipingDataToCommand:(NSString *)command16 - (BOOL)_extractArchivePipingDataToCommand:(NSString *)command 17 17 { 18 18 // Get the file size. … … 53 53 } 54 54 55 - (BOOL)_extractTAR :(NSString *)archivePath55 - (BOOL)_extractTAR 56 56 { 57 return [self _extractArchiveP ath:archivePath pipingDataToCommand:@"tar -xC \"$DESTINATION\""];57 return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""]; 58 58 } 59 59 60 - (BOOL)_extractTGZ :(NSString *)archivePath60 - (BOOL)_extractTGZ 61 61 { 62 return [self _extractArchiveP ath:archivePath pipingDataToCommand:@"tar -zxC \"$DESTINATION\""];62 return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""]; 63 63 } 64 64 65 - (BOOL)_extractTBZ :(NSString *)archivePath65 - (BOOL)_extractTBZ 66 66 { 67 return [self _extractArchiveP ath:archivePath pipingDataToCommand:@"tar -jxC \"$DESTINATION\""];67 return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""]; 68 68 } 69 69 70 - (BOOL)_extractZIP :(NSString *)archivePath70 - (BOOL)_extractZIP 71 71 { 72 return [self _extractArchiveP ath:archivePath pipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""];72 return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""]; 73 73 } 74 74 75 - (BOOL)_extractDMG :(NSString *)archivePath75 - (BOOL)_extractDMG 76 76 { 77 77 // get a unique mount point path … … 100 100 } 101 101 102 - (void)_unarchive Path:(NSString *)path102 - (void)_unarchive 103 103 { 104 104 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; … … 108 108 // we should continue with the update; returns NO if an error occurred. 109 109 NSDictionary *commandDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 110 @"_extractTBZ :", @".tbz",111 @"_extractTBZ :", @".tar.bz2",112 @"_extractTGZ :", @".tgz",113 @"_extractTGZ :", @".tar.gz",114 @"_extractTAR :", @".tar",115 @"_extractZIP :", @".zip",116 @"_extractDMG :", @".dmg",110 @"_extractTBZ", @".tbz", 111 @"_extractTBZ", @".tar.bz2", 112 @"_extractTGZ", @".tgz", 113 @"_extractTGZ", @".tar.gz", 114 @"_extractTAR", @".tar", 115 @"_extractZIP", @".zip", 116 @"_extractDMG", @".dmg", 117 117 nil]; 118 118 SEL command = NULL; 119 NSString *theLastPathComponent = [[ path lastPathComponent] lowercaseString];119 NSString *theLastPathComponent = [[archivePath lastPathComponent] lowercaseString]; 120 120 NSEnumerator *theEnumerator = [[commandDictionary allKeys] objectEnumerator]; 121 121 NSString *theExtension = NULL; 122 122 while ((theExtension = [theEnumerator nextObject]) != NULL) 123 { 124 if ([[theLastPathComponent substringFromIndex:[theLastPathComponent length] - [theExtension length]] isEqualToString:theExtension]) 123 125 { 124 if ([[theLastPathComponent substringFromIndex:[theLastPathComponent length] - [theExtension length]] isEqualToString:theExtension])125 {126 126 command = NSSelectorFromString([commandDictionary objectForKey:theExtension]); 127 127 break; 128 }129 128 } 129 } 130 130 131 131 BOOL result; … … 134 134 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:command]]; 135 135 [invocation setSelector:command]; 136 [invocation setArgument:&path atIndex:2]; // 0 and 1 are private!137 136 [invocation invokeWithTarget:self]; 138 137 [invocation getReturnValue:&result]; … … 157 156 - (void)unarchivePath:(NSString *)path 158 157 { 159 [NSThread detachNewThreadSelector:@selector(_unarchivePath:) toTarget:self withObject:path]; 158 archivePath = [path copy]; 159 [NSThread detachNewThreadSelector:@selector(_unarchive) toTarget:self withObject:nil]; 160 160 } 161 161 … … 165 165 } 166 166 167 - (void)cleanUp 168 { 169 if ([[archivePath pathExtension] isEqualToString:@".dmg"]) 170 { 171 [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", [archivePath stringByDeletingLastPathComponent], @"-force", nil]]; 172 } 173 [[NSFileManager defaultManager] removeFileAtPath:archivePath handler:nil]; 174 } 175 176 - (void)dealloc 177 { 178 [archivePath release]; 179 [super dealloc]; 180 } 181 167 182 @end trunk/SUUpdater.h
r114 r147 16 16 // .zip, .dmg, .tar, .tbz, .tgz archives are supported at this time. 17 17 18 @class SUAppcastItem, SUUpdateAlert, SUStatusController ;18 @class SUAppcastItem, SUUpdateAlert, SUStatusController, SUUnarchiver; 19 19 @interface SUUpdater : NSObject { 20 20 SUAppcastItem *updateItem; … … 34 34 NSBundle *hostBundle; 35 35 id delegate; 36 37 SUUnarchiver *unarchiver; 36 38 } 37 39 trunk/SUUpdater.m
r144 r147 437 437 438 438 - (void)unarchiverDidFinish:(SUUnarchiver *)ua 439 { 440 [ua autorelease]; 441 439 { 442 440 if ([self isAutomaticallyUpdating]) 443 441 { … … 455 453 - (void)unarchiverDidFail:(SUUnarchiver *)ua 456 454 { 457 [ua autorelease];455 [ua release]; 458 456 [self showUpdateErrorAlertWithInfo:SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil)]; 459 457 [self abandonUpdate]; … … 478 476 } 479 477 480 SUUnarchiver *unarchiver = [[SUUnarchiver alloc] init];478 unarchiver = [[SUUnarchiver alloc] init]; 481 479 [unarchiver setDelegate:self]; 482 480 [unarchiver unarchivePath:downloadPath]; // asynchronous extraction! … … 495 493 [statusController beginActionWithTitle:SULocalizedString(@"Installing update...", nil) maxProgressValue:0 statusText:nil]; 496 494 [statusController setButtonEnabled:NO]; 497 498 // Hack to force us to wait for the UI to update.499 NSEvent *event;500 while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]))501 [NSApp sendEvent:event];502 503 // Search subdirectories for the application504 NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[hostBundle bundlePath] lastPathComponent];505 BOOL isPackage = NO;506 NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:[downloadPath stringByDeletingLastPathComponent]];507 while ((currentFile = [dirEnum nextObject]))508 {509 // Some DMGs have symlinks into /Applications! That's no good! And there's no point in looking in bundles.510 if ([[NSFileManager defaultManager] isAliasFolderAtPath:[[downloadPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:currentFile]] ||511 [[currentFile pathExtension] isEqualToString:[[hostBundle bundlePath] pathExtension]] ||512 [[currentFile pathExtension] isEqualToString:@"pkg"] ||513 [[currentFile pathExtension] isEqualToString:@"mpkg"])514 {515 [dirEnum skipDescendents];516 }517 518 if ([[currentFile lastPathComponent] isEqualToString:bundleFileName]) // We found one!519 {520 isPackage = NO;521 newAppDownloadPath = [[downloadPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:currentFile];522 break;523 }524 else if (([[currentFile pathExtension] isEqualToString:@"pkg"] || [[currentFile pathExtension] isEqualToString:@"mpkg"]) &&525 [[[currentFile lastPathComponent] stringByDeletingPathExtension] isEqualToString:[bundleFileName stringByDeletingPathExtension]])526 {527 isPackage = YES;528 newAppDownloadPath = [[downloadPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:currentFile];529 break;530 }531 }532 533 if (![[NSFileManager defaultManager] fileExistsAtPath:newAppDownloadPath])534 {535 NSLog(@"The update archive didn't contain an application or package with the proper name: %@ or %@.", bundleFileName, [[bundleFileName stringByDeletingPathExtension] stringByAppendingPathComponent:@".[m]pkg"]);536 [self showUpdateErrorAlertWithInfo:SULocalizedString(@"An error occurred during installation. Please try again later.", nil)];537 [self abandonUpdate];538 return;539 }540 541 // Alright, *now* we can actually install the new version.542 int processIdentifierToWatch = [[NSProcessInfo processInfo] processIdentifier];543 if (isPackage)544 {545 NSString *installerPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:@"com.apple.installer"];546 installerPath = [installerPath stringByAppendingString:@"/Contents/MacOS/Installer"];547 NSTask *installer = [NSTask launchedTaskWithLaunchPath:installerPath arguments:[NSArray arrayWithObjects:newAppDownloadPath, nil]];548 processIdentifierToWatch = [installer processIdentifier]; // We want to wait until the installer quits.549 }550 else551 {552 if (![[NSFileManager defaultManager] copyPath:newAppDownloadPath553 overPath:[hostBundle bundlePath]554 withAuthentication:![self isAutomaticallyUpdating]])555 {556 [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"%@ can't install the update! Make sure you have enough disk space.", nil), [hostBundle name]]];557 [self abandonUpdate];558 return;559 }560 }561 495 562 496 // Prompt for permission to restart if we're automatically updating. … … 571 505 } 572 506 573 // This is really sloppy and coupled, but I can't think of a better way to deal with this. 574 // If we've got a DMG, we've mounted it; now we've got to unmount it. 575 if ([[[downloadPath pathExtension] lowercaseString] isEqualToString:@"dmg"]) 576 { 577 [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", [newAppDownloadPath stringByDeletingLastPathComponent], @"-force", nil]]; 578 } 579 507 [SUInstaller installFromUpdateFolder:[downloadPath stringByDeletingLastPathComponent] overHostBundle:hostBundle delegate:self]; 508 } 509 510 - (void)installerFinishedForHostBundle:(NSBundle *)hb 511 { 512 if (hb != hostBundle) { return; } 513 514 [unarchiver cleanUp]; 580 515 [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self]; 581 516 582 517 NSString *relaunchPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"relaunch" ofType:nil]; 583 @try { 584 [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:[hostBundle bundlePath], [NSString stringWithFormat:@"%d", processIdentifierToWatch], nil]]; 585 } @catch (NSException *e) { 586 NSLog(@"relaunch error: %@", e); 518 @try 519 { 520 [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:[hostBundle bundlePath], [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], nil]]; 521 } 522 @catch (NSException *e) 523 { 524 NSLog(@"Sparkle relaunch error: %@", e); 525 [self showUpdateErrorAlertWithInfo:[NSString stringWithFormat:SULocalizedString(@"Couldn't restart %1$@, but the new version will be available next time you run %1$@.", nil), [hostBundle name]]]; 526 [self abandonUpdate]; 587 527 } 588 528 [NSApp terminate:self]; 529 } 530 531 - (void)installerForHostBundle:(NSBundle *)hb failedWithError:(NSError *)error 532 { 533 if (hb != hostBundle) { return; } 534 NSLog(@"Sparkle Installation Error: %@", [error localizedDescription]); 535 [self showUpdateErrorAlertWithInfo:SULocalizedString(@"An error occurred during installation. Please try again later.", nil)]; 536 [self abandonUpdate]; 589 537 } 590 538 trunk/Sparkle.h
r127 r147 40 40 #import "SUAutomaticUpdateAlert.h" 41 41 #import "SUConstants.h" 42 #import "SUInstaller.h" 42 43 #import "SUStandardVersionComparator.h" 43 44 #import "SUStatusChecker.h" trunk/Sparkle.xcodeproj/project.pbxproj
r146 r147 35 35 6171D9070D57B81800BFE886 /* NSFileManager+Aliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 6171D9050D57B81800BFE886 /* NSFileManager+Aliases.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36 36 6171D9080D57B81800BFE886 /* NSFileManager+Aliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 6171D9060D57B81800BFE886 /* NSFileManager+Aliases.m */; }; 37 618FA5010DAE88B40026945C /* SUInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA4FF0DAE88B40026945C /* SUInstaller.h */; }; 38 618FA5020DAE88B40026945C /* SUInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5000DAE88B40026945C /* SUInstaller.m */; }; 39 618FA5050DAE8AB80026945C /* SUPlainInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA5030DAE8AB80026945C /* SUPlainInstaller.h */; }; 40 618FA5060DAE8AB80026945C /* SUPlainInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */; }; 41 618FA5220DAE8E8A0026945C /* SUPackageInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA5200DAE8E8A0026945C /* SUPackageInstaller.h */; }; 42 618FA5230DAE8E8A0026945C /* SUPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */; }; 37 43 6196CFF909C72148000DC222 /* SUStatusController.h in Headers */ = {isa = PBXBuildFile; fileRef = 6196CFE309C71ADE000DC222 /* SUStatusController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 38 44 6196CFFA09C72149000DC222 /* SUStatusController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6196CFE409C71ADE000DC222 /* SUStatusController.m */; }; … … 150 156 6171D9050D57B81800BFE886 /* NSFileManager+Aliases.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+Aliases.h"; sourceTree = "<group>"; }; 151 157 6171D9060D57B81800BFE886 /* NSFileManager+Aliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+Aliases.m"; sourceTree = "<group>"; }; 158 618FA4FF0DAE88B40026945C /* SUInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUInstaller.h; sourceTree = "<group>"; }; 159 618FA5000DAE88B40026945C /* SUInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUInstaller.m; sourceTree = "<group>"; }; 160 618FA5030DAE8AB80026945C /* SUPlainInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUPlainInstaller.h; sourceTree = "<group>"; }; 161 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUPlainInstaller.m; sourceTree = "<group>"; }; 162 618FA5200DAE8E8A0026945C /* SUPackageInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUPackageInstaller.h; sourceTree = "<group>"; }; 163 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUPackageInstaller.m; sourceTree = "<group>"; }; 152 164 6196CFE309C71ADE000DC222 /* SUStatusController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUStatusController.h; sourceTree = "<group>"; }; 153 165 6196CFE409C71ADE000DC222 /* SUStatusController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUStatusController.m; sourceTree = "<group>"; }; … … 366 378 61299B3809CB053D00B7442F /* Cocoa Categories */, 367 379 61299B3A09CB056100B7442F /* User Interface */, 380 618FA6DB0DB485440026945C /* Installation */, 368 381 61B5F8F309C4CE5900B25A18 /* Utilities */, 369 382 61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */, … … 430 443 ); 431 444 name = "User Interface";&nb