| 1 | // |
|---|
| 2 | // SUInstaller.m |
|---|
| 3 | // Sparkle |
|---|
| 4 | // |
|---|
| 5 | // Created by Andy Matuschak on 4/10/08. |
|---|
| 6 | // Copyright 2008 Andy Matuschak. All rights reserved. |
|---|
| 7 | // |
|---|
| 8 | |
|---|
| 9 | #import "SUInstaller.h" |
|---|
| 10 | #import "SUPlainInstaller.h" |
|---|
| 11 | #import "SUPackageInstaller.h" |
|---|
| 12 | #import "SUHost.h" |
|---|
| 13 | |
|---|
| 14 | @implementation SUInstaller |
|---|
| 15 | |
|---|
| 16 | + (BOOL)_isAliasFolderAtPath:(NSString *)path |
|---|
| 17 | { |
|---|
| 18 | FSRef fileRef; |
|---|
| 19 | OSStatus err = noErr; |
|---|
| 20 | Boolean aliasFileFlag, folderFlag; |
|---|
| 21 | NSURL *fileURL = [NSURL fileURLWithPath:path]; |
|---|
| 22 | |
|---|
| 23 | if (FALSE == CFURLGetFSRef((CFURLRef)fileURL, &fileRef)) |
|---|
| 24 | err = coreFoundationUnknownErr; |
|---|
| 25 | |
|---|
| 26 | if (noErr == err) |
|---|
| 27 | err = FSIsAliasFile(&fileRef, &aliasFileFlag, &folderFlag); |
|---|
| 28 | |
|---|
| 29 | if (noErr == err) |
|---|
| 30 | return (BOOL)(aliasFileFlag && folderFlag); |
|---|
| 31 | else |
|---|
| 32 | return NO; |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | + (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator updater:(SUUpdater*)updater |
|---|
| 37 | { |
|---|
| 38 | // Search subdirectories for the application |
|---|
| 39 | NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[host bundlePath] lastPathComponent], *alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]]; |
|---|
| 40 | BOOL isPackage = NO; |
|---|
| 41 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:updateFolder]; |
|---|
| 42 | while ((currentFile = [dirEnum nextObject])) |
|---|
| 43 | { |
|---|
| 44 | NSString *currentPath = [updateFolder stringByAppendingPathComponent:currentFile]; |
|---|
| 45 | if ([[currentFile lastPathComponent] isEqualToString:bundleFileName] || |
|---|
| 46 | [[currentFile lastPathComponent] isEqualToString:alternateBundleFileName]) // We found one! |
|---|
| 47 | { |
|---|
| 48 | isPackage = NO; |
|---|
| 49 | newAppDownloadPath = currentPath; |
|---|
| 50 | break; |
|---|
| 51 | } |
|---|
| 52 | else if (([[currentFile pathExtension] isEqualToString:@"pkg"] || [[currentFile pathExtension] isEqualToString:@"mpkg"]) && |
|---|
| 53 | [[[currentFile lastPathComponent] stringByDeletingPathExtension] isEqualToString:[bundleFileName stringByDeletingPathExtension]]) |
|---|
| 54 | { |
|---|
| 55 | isPackage = YES; |
|---|
| 56 | newAppDownloadPath = currentPath; |
|---|
| 57 | break; |
|---|
| 58 | } |
|---|
| 59 | else |
|---|
| 60 | { |
|---|
| 61 | // Try matching on bundle identifiers in case the user has changed the name of the host app |
|---|
| 62 | NSBundle *incomingBundle = [NSBundle bundleWithPath:currentPath]; |
|---|
| 63 | if(incomingBundle && [[incomingBundle bundleIdentifier] isEqualToString:[[host bundle] bundleIdentifier]]) |
|---|
| 64 | { |
|---|
| 65 | isPackage = NO; |
|---|
| 66 | newAppDownloadPath = currentPath; |
|---|
| 67 | break; |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | // Some DMGs have symlinks into /Applications! That's no good! And there's no point in looking in bundles. |
|---|
| 72 | if ([self _isAliasFolderAtPath:currentPath] || |
|---|
| 73 | [[currentFile pathExtension] isEqualToString:[[host bundlePath] pathExtension]] || |
|---|
| 74 | [[currentFile pathExtension] isEqualToString:@"pkg"] || |
|---|
| 75 | [[currentFile pathExtension] isEqualToString:@"mpkg"]) |
|---|
| 76 | { |
|---|
| 77 | [dirEnum skipDescendents]; |
|---|
| 78 | } |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | if (newAppDownloadPath == nil) |
|---|
| 82 | { |
|---|
| 83 | [self _finishInstallationWithResult:NO host:host error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find an appropriate update in the downloaded package." forKey:NSLocalizedDescriptionKey]] delegate:delegate]; |
|---|
| 84 | } |
|---|
| 85 | else |
|---|
| 86 | { |
|---|
| 87 | if ([delegate respondsToSelector:@selector(installUpdateWithPath:isPackage:)]) { |
|---|
| 88 | if ([delegate installUpdateWithPath:newAppDownloadPath isPackage:isPackage]) { |
|---|
| 89 | [self _finishInstallationWithResult:YES host:host error:NULL delegate:delegate]; |
|---|
| 90 | return; |
|---|
| 91 | } |
|---|
| 92 | } |
|---|
| 93 | [(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously versionComparator:comparator]; |
|---|
| 94 | } |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | + (void)_mdimportHost:(SUHost *)host |
|---|
| 98 | { |
|---|
| 99 | NSTask *mdimport = [[[NSTask alloc] init] autorelease]; |
|---|
| 100 | [mdimport setLaunchPath:@"/usr/bin/mdimport"]; |
|---|
| 101 | [mdimport setArguments:[NSArray arrayWithObject:[host bundlePath]]]; |
|---|
| 102 | [mdimport launch]; |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | + (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate |
|---|
| 106 | { |
|---|
| 107 | if (result == YES) |
|---|
| 108 | { |
|---|
| 109 | [self _mdimportHost:host]; |
|---|
| 110 | if ([delegate respondsToSelector:@selector(installerFinishedForHost:)]) |
|---|
| 111 | [delegate installerFinishedForHost:host]; |
|---|
| 112 | } |
|---|
| 113 | else |
|---|
| 114 | { |
|---|
| 115 | if ([delegate respondsToSelector:@selector(installerForHost:failedWithError:)]) |
|---|
| 116 | [delegate installerForHost:host failedWithError:error]; |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | @end |
|---|