Changeset 153
- Timestamp:
- 05/07/08 16:12:41 (1 week ago)
- Files:
-
- trunk/NSFileManager+Aliases.h (modified) (2 diffs)
- trunk/NSFileManager+Authentication.m (modified) (2 diffs)
- trunk/NSFileManager+ExtendedAttributes.h (modified) (2 diffs)
- trunk/NSURL+Parameters.h (added)
- trunk/NSURL+Parameters.m (added)
- trunk/NSWorkspace+SystemVersion.h (added)
- trunk/NSWorkspace+SystemVersion.m (added)
- trunk/RSS.h (modified) (1 diff)
- trunk/RSS.m (modified) (2 diffs)
- trunk/SUAppcast.h (modified) (2 diffs)
- trunk/SUAppcast.m (modified) (3 diffs)
- trunk/SUAppcastItem.h (modified) (2 diffs)
- trunk/SUAppcastItem.m (modified) (5 diffs)
- trunk/SUAutomaticUpdateAlert.h (modified) (1 diff)
- trunk/SUAutomaticUpdateAlert.m (modified) (4 diffs)
- trunk/SUAutomaticUpdateDriver.h (added)
- trunk/SUAutomaticUpdateDriver.m (added)
- trunk/SUBasicUpdateDriver.h (added)
- trunk/SUBasicUpdateDriver.m (added)
- trunk/SUConstants.h (modified) (2 diffs)
- trunk/SUConstants.m (modified) (2 diffs)
- trunk/SUInstaller.h (modified) (1 diff)
- trunk/SUInstaller.m (modified) (2 diffs)
- trunk/SUPackageInstaller.h (modified) (2 diffs)
- trunk/SUPlainInstaller.h (modified) (1 diff)
- trunk/SUPlainInstaller.m (modified) (3 diffs)
- trunk/SUProbingUpdateDriver.h (added)
- trunk/SUProbingUpdateDriver.m (added)
- trunk/SUScheduledUpdateDriver.h (added)
- trunk/SUScheduledUpdateDriver.m (added)
- trunk/SUStatusChecker.h (deleted)
- trunk/SUStatusChecker.m (deleted)
- trunk/SUSystemProfiler.m (modified) (1 diff)
- trunk/SUUpdateDriver.h (added)
- trunk/SUUpdateDriver.m (added)
- trunk/SUUpdatePermissionPrompt.h (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/info.nib (modified) (1 diff)
- trunk/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib (modified) (previous)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/NSFileManager+Aliases.h
r121 r153 7 7 // 8 8 9 #ifndef NSFILEMANAGER_PLUS_ALIASES_H 10 #define NSFILEMANAGER_PLUS_ALIASES_H 11 9 12 #import <Cocoa/Cocoa.h> 10 13 … … 12 15 - (BOOL)isAliasFolderAtPath:(NSString *)path; 13 16 @end 17 18 #endif trunk/NSFileManager+Authentication.m
r147 r153 89 89 else 90 90 postFix = @"old"; 91 return [[[path stringByDeletingPathExtension] stringByAppendingFormat:@" (%@)", postFix] stringByAppendingPathExtension:[path pathExtension]]; 91 NSString *prefix = [[path stringByDeletingPathExtension] stringByAppendingFormat:@" (%@)", postFix]; 92 NSString *tempDir = [prefix stringByAppendingPathExtension:[path pathExtension]]; 93 // Now let's make sure we get a unique path. 94 int cnt=2; 95 while ([[NSFileManager defaultManager] fileExistsAtPath:tempDir] && cnt <= 999999) 96 tempDir = [NSString stringWithFormat:@"%@ %d.%@", prefix, cnt++, [path pathExtension]]; 97 return tempDir; 92 98 } 93 99 … … 207 213 208 214 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 } 215 216 if (![[NSFileManager defaultManager] movePath:dst toPath:tmpPath handler:self]) 217 { 218 if (error != NULL) 219 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Couldn't move %@ to %@.", dst, tmpPath] forKey:NSLocalizedDescriptionKey]]; 220 return NO; 221 } 222 if (![[NSFileManager defaultManager] copyPath:src toPath:dst handler:self]) 223 { 224 if (error != NULL) 225 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Couldn't copy %@ to %@.", src, dst] forKey:NSLocalizedDescriptionKey]]; 226 return NO; 230 227 } 231 228 trunk/NSFileManager+ExtendedAttributes.h
r143 r153 6 6 // Copyright 2008 Mark Mentovai. All rights reserved. 7 7 // 8 9 #ifndef NSFILEMANAGER_PLUS_EXTENDEDATTRIBUTES 10 #define NSFILEMANAGER_PLUS_EXTENDEDATTRIBUTES 8 11 9 12 #import <Cocoa/Cocoa.h> … … 47 50 48 51 @end 52 53 #endif trunk/RSS.h
r99 r153 62 62 63 63 /*Public*/ 64 - (RSS *)initWithURL:(NSURL *) url normalize:(BOOL) fl userAgent:(NSString*)userAgent error:(NSError **)error; 64 65 65 66 - (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) description; 66 67 67 68 - (RSS *) initWithData: (NSData *) rssData normalize: (BOOL) fl; 68 69 - (RSS *) initWithURL: (NSURL *) url normalize: (BOOL) fl;70 - (RSS *) initWithURL: (NSURL *) url normalize: (BOOL) fl userAgent:(NSString *)userAgent;71 69 72 70 - (NSDictionary *) headerItems; trunk/RSS.m
r105 r153 139 139 140 140 141 - (RSS *) initWithURL: (NSURL *) url normalize: (BOOL) fl 141 142 143 - (RSS *)initWithURL:(NSURL *)url normalize:(BOOL)fl userAgent:(NSString*)userAgent error:(NSError **)error 142 144 { 143 return [self initWithURL: url normalize: fl userAgent: nil];144 }145 146 147 148 - (RSS *) initWithURL: (NSURL *) url normalize: (BOOL) fl userAgent: (NSString*)userAgent149 {150 NSData *rssData;151 152 145 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url cachePolicy: NSURLRequestReloadIgnoringCacheData 153 146 timeoutInterval: 30.0]; … … 155 148 [request setValue: userAgent forHTTPHeaderField: @"User-Agent"]; 156 149 157 NSURLResponse *response=0; 158 NSError *error=0; 159 160 rssData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error]; 161 162 if (rssData == nil) 150 NSURLResponse *response = nil; 151 NSData *rssData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error]; 152 if (rssData == nil) { return nil; } 153 154 @try 163 155 { 164 NSString *failureReason; 165 if ([error respondsToSelector:@selector(localizedFailureReason)]) 166 failureReason = [error localizedFailureReason]; 167 else 168 failureReason = [error localizedDescription]; 169 NSException *exception = [NSException exceptionWithName: @"RSSDownloadFailed" 170 reason: failureReason userInfo: [error userInfo] ]; 171 [exception raise]; 156 [self initWithData:rssData normalize:fl]; 172 157 } 173 174 return [self initWithData: rssData normalize: fl]; 175 } /*initWithUrl*/ 158 @catch (NSException *parseException) 159 { 160 if (error) 161 *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], NSLocalizedFailureReasonErrorKey, nil]]; 162 return nil; 163 } 164 return self; 165 } 176 166 177 167 trunk/SUAppcast.h
r114 r153 13 13 @interface SUAppcast : NSObject { 14 14 NSArray *items; 15 NSString *userAgentString; 15 16 id delegate; 16 17 } 17 18 18 - (void)fetchAppcastFromURL:(NSURL *)url parameters:(NSArray *)parameters;19 - (void)fetchAppcastFromURL:(NSURL *)url; 19 20 - (void)setDelegate:delegate; 21 - (void)setUserAgentString:(NSString *)userAgentString; 20 22 21 23 - (SUAppcastItem *)newestItem; … … 26 28 @interface NSObject (SUAppcastDelegate) 27 29 - (void)appcastDidFinishLoading:(SUAppcast *)appcast; 28 - (void)appcastDidFailToLoad:(SUAppcast *)appcast; 29 - (NSString *)userAgentForAppcast:(SUAppcast *)appcast; 30 - (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error; 30 31 @end 31 32 trunk/SUAppcast.m
r114 r153 10 10 #import "SUAppcast.h" 11 11 12 @interface NSURL (SUParameterAdditions)13 - (NSURL *)URLWithParameters:(NSArray *)parameters;14 @end15 16 @implementation NSURL (SUParameterAdditions)17 - (NSURL *)URLWithParameters:(NSArray *)parameters;18 {19 if (parameters == nil || [parameters count] == 0) { return self; }20 NSMutableArray *profileInfo = [NSMutableArray array];21 NSEnumerator *profileInfoEnumerator = [parameters objectEnumerator];22 NSDictionary *currentProfileInfo;23 while ((currentProfileInfo = [profileInfoEnumerator nextObject])) {24 [profileInfo addObject:[NSString stringWithFormat:@"%@=%@", [currentProfileInfo objectForKey:@"key"], [currentProfileInfo objectForKey:@"value"]]];25 }26 27 NSString *appcastStringWithProfile = [NSString stringWithFormat:@"%@?%@", [self absoluteString], [profileInfo componentsJoinedByString:@"&"]];28 29 // Clean it up so it's a valid URL30 return [NSURL URLWithString:[appcastStringWithProfile stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];31 }32 @end33 34 35 12 @implementation SUAppcast 36 13 37 - (void)fetchAppcastFromURL:(NSURL *)url parameters:(NSArray *)parameters14 - (void)fetchAppcastFromURL:(NSURL *)url 38 15 { 39 [NSThread detachNewThreadSelector:@selector(_fetchAppcastFromURL:) toTarget:self withObject:[url URLWithParameters:parameters]]; // let's not block the main thread 40 } 41 42 - (void)setDelegate:del 43 { 44 delegate = del; 16 [NSThread detachNewThreadSelector:@selector(_fetchAppcastFromURL:) toTarget:self withObject:url]; 45 17 } 46 18 … … 53 25 - (SUAppcastItem *)newestItem 54 26 { 55 return [items objectAtIndex:0]; // the RSS class takes care of sorting by published date, descending. 27 if ([items count] > 0) 28 return [items objectAtIndex:0]; // the RSS class takes care of sorting by published date, descending. 29 else 30 return nil; 56 31 } 57 32 … … 65 40 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 66 41 67 RSS *feed = [RSS alloc]; 42 NSError *error = nil; 43 RSS *feed = [[RSS alloc] initWithURL:url normalize:YES userAgent:userAgentString error:&error]; 44 if (!feed) 45 { 46 [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO]; 47 return; 48 } 49 50 // Set up all the appcast items: 51 items = [NSMutableArray array]; 52 id enumerator = [[feed newsItems] objectEnumerator], current; 68 53 @try 69 54 { 70 NSString *userAgent = nil;71 if ([delegate respondsToSelector:@selector(userAgentForAppcast:)])72 userAgent = [delegate userAgentForAppcast:self];73 74 feed = [feed initWithURL:url normalize:YES userAgent:userAgent];75 if (!feed)76 [NSException raise:@"SUFeedException" format:@"Couldn't fetch feed from server."];77 78 // Set up all the appcast items79 NSMutableArray *tempItems = [NSMutableArray array];80 id enumerator = [[feed newsItems] objectEnumerator], current;81 55 while ((current = [enumerator nextObject])) 82 56 { 83 [ tempItems addObject:[[[SUAppcastItem alloc] initWithDictionary:current] autorelease]];57 [(NSMutableArray *)items addObject:[[[SUAppcastItem alloc] initWithDictionary:current] autorelease]]; 84 58 } 85 items = [[NSArray arrayWithArray:tempItems] retain]; 59 } 60 @catch (NSException *parseException) 61 { 62 error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], SUTechnicalErrorInformationKey, nil]]; 63 [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO]; 64 return; 65 } 66 items = [[NSArray arrayWithArray:items] retain]; // Make the items list immutable. 67 68 if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)]) 69 [delegate performSelectorOnMainThread:@selector(appcastDidFinishLoading:) withObject:self waitUntilDone:NO]; 86 70 87 if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)]) 88 [delegate performSelectorOnMainThread:@selector(appcastDidFinishLoading:) withObject:self waitUntilDone:NO]; 89 90 } 91 @catch (NSException *e) 71 [feed release]; 72 [pool release]; 73 } 74 75 - (void)reportError:(NSError *)error 76 { 77 if ([delegate respondsToSelector:@selector(appcast:failedToLoadWithError:)]) 92 78 { 93 if ([delegate respondsToSelector:@selector(appcastDidFailToLoad:)]) 94 [delegate performSelectorOnMainThread:@selector(appcastDidFailToLoad:) withObject:self waitUntilDone:NO]; 95 } 96 @finally 97 { 98 [feed release]; 99 [pool release]; 79 [delegate appcast:self failedToLoadWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred in retrieving update information. Please try again later.", nil), NSLocalizedDescriptionKey, [error localizedDescription], NSLocalizedFailureReasonErrorKey, nil]]]; 100 80 } 101 81 } 102 82 83 - (void)setUserAgentString:(NSString *)uas 84 { 85 [userAgentString release]; 86 userAgentString = [uas copy]; 87 } 88 89 - (void)setDelegate:del 90 { 91 delegate = del; 92 } 93 103 94 @end trunk/SUAppcastItem.h
r99 r153 18 18 NSURL *releaseNotesURL; 19 19 20 NSString *DSASignature; 21 NSString *MD5Sum; 22 20 NSString *DSASignature; 23 21 NSString *minimumSystemVersion; 24 22 25 23 NSURL *fileURL; 26 NSString *fileVersion;27 24 NSString *versionString; 25 NSString *displayVersionString; 28 26 } 29 27 … … 32 30 33 31 - (NSString *)title; 34 - ( void)setTitle:(NSString *)aTitle;35 32 - (NSString *)versionString; 33 - (NSString *)displayVersionString; 36 34 - (NSDate *)date; 37 - (void)setDate:(NSDate *)aDate;38 39 35 - (NSString *)description; 40 - (void)setDescription:(NSString *)aDescription;41 42 36 - (NSURL *)releaseNotesURL; 43 - (void)setReleaseNotesURL:(NSURL *)aReleaseNotesURL; 44 37 - (NSURL *)fileURL; 45 38 - (NSString *)DSASignature; 46 - (void)setDSASignature:(NSString *)aDSASignature;47 48 - (NSString *)MD5Sum;49 - (void)setMD5Sum:(NSString *)aMd5Sum;50 51 - (NSURL *)fileURL;52 - (void)setFileURL:(NSURL *)aFileURL;53 54 - (NSString *)fileVersion;55 - (void)setFileVersion:(NSString *)aFileVersion;56 57 - (NSString *)versionString;58 - (void)setVersionString:(NSString *)versionString;59 60 39 - (NSString *)minimumSystemVersion; 61 - (void)setMinimumSystemVersion:(NSString *)systemVersionString;62 40 63 41 @end trunk/SUAppcastItem.m
r110 r153 11 11 12 12 @implementation SUAppcastItem 13 14 - initWithDictionary:(NSDictionary *)dict15 {16 self = [super init];17 if (self)18 {19 [self setTitle:[dict objectForKey:@"title"]];20 [self setDate:[dict objectForKey:@"pubDate"]];21 [self setDescription:[dict objectForKey:@"description"]];22 23 id enclosure = [dict objectForKey:@"enclosure"];24 [self setDSASignature:[enclosure objectForKey:@"sparkle:dsaSignature"]];25 [self setMD5Sum:[enclosure objectForKey:@"sparkle:md5Sum"]];26 27 [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];28 29 // Find the appropriate release notes URL.30 if ([dict objectForKey:@"sparkle:releaseNotesLink"])31 {32 [self setReleaseNotesURL:[NSURL URLWithString:[dict objectForKey:@"sparkle:releaseNotesLink"]]];33 }34 else if ([[self description] hasPrefix:@"http://"]) // if the description starts with http://, use that.35 {36 [self setReleaseNotesURL:[NSURL URLWithString:[self description]]];37 }38 else39 {40 [self setReleaseNotesURL:nil];41 }42 43 NSString *minVersion = [dict objectForKey:@"sparkle:minimumSystemVersion"];44 if(minVersion)45 [self setMinimumSystemVersion:minVersion];46 else47 [self setMinimumSystemVersion:@"10.3.0"];//sparkle doesn't run on 10.2-, so we don't have to worry about it48 49 // Try to find a version string.50 // Finding the new version number from the RSS feed is a little bit hacky. There are two ways:51 // 1. A "sparkle:version" attribute on the enclosure tag, an extension from the RSS spec.52 // 2. If there isn't a version attribute, Sparkle will parse the path in the enclosure, expecting53 // that it will look like this: http://something.com/YourApp_0.5.zip. It'll read whatever's between the last54 // underscore and the last period as the version number. So name your packages like this: APPNAME_VERSION.extension.55 // The big caveat with this is that you can't have underscores in your version strings, as that'll confuse Sparkle.56 // Feel free to change the separator string to a hyphen or something more suited to your needs if you like.57 NSString *newVersion = [enclosure objectForKey:@"sparkle:version"];58 if (!newVersion) // no sparkle:version attribute59 {60 // Separate the url by underscores and take the last component, as that'll be closest to the end,61 // then we remove the extension. Hopefully, this will be the version.62 NSArray *fileComponents = [[enclosure objectForKey:@"url"] componentsSeparatedByString:@"_"];63 if ([fileComponents count] > 1)64 newVersion = [[fileComponents lastObject] stringByDeletingPathExtension];65 }66 [self setFileVersion:newVersion];67 68 NSString *shortVersionString = [enclosure objectForKey:@"sparkle:shortVersionString"];69 if (shortVersionString)70 [self setVersionString:shortVersionString];71 else72 [self setVersionString:[self fileVersion]];73 }74 75 return self;76 }77 13 78 14 // Attack of accessors! … … 121 57 DSASignature = [aDSASignature copy]; 122 58 } 123 124 125 - (NSString *)MD5Sum { return [[MD5Sum retain] autorelease]; } 126 127 - (void)setMD5Sum:(NSString *)aMD5Sum 128 { 129 [MD5Sum release]; 130 MD5Sum = [aMD5Sum copy]; 131 } 132 59 133 60 134 61 - (NSURL *)fileURL { return [[fileURL retain] autorelease]; } … … 141 68 142 69 143 - (NSString *) fileVersion { return [[fileVersionretain] autorelease]; }70 - (NSString *)versionString { return [[versionString retain] autorelease]; } 144 71 145 - (void)set FileVersion:(NSString *)aFileVersion72 - (void)setVersionString:(NSString *)s 146 73 { 147 [ fileVersionrelease];148 fileVersion = [aFileVersioncopy];74 [versionString release]; 75 versionString = [s copy]; 149 76 } 150 77 151 78 152 - (NSString *) versionString { return [[versionString retain] autorelease]; }79 - (NSString *)displayVersionString { return [[displayVersionString retain] autorelease]; } 153 80 154 - (void)set VersionString:(NSString *)aVersionString81 - (void)setDisplayVersionString:(NSString *)s 155 82 { 156 [ versionString release];157 versionString = [aVersionStringcopy];83 [displayVersionString release]; 84 displayVersionString = [s copy]; 158 85 } 159 86 … … 166 93 } 167 94 95 - initWithDictionary:(NSDictionary *)dict 96 { 97 self = [super init]; 98 if (self) 99 { 100 [self setTitle:[dict objectForKey:@"title"]]; 101 [self setDate:[dict objectForKey:@"pubDate"]]; 102 [self setDescription:[dict objectForKey:@"description"]]; 103 104 id enclosure = [dict objectForKey:@"enclosure"]; 105 if (enclosure == nil || [enclosure objectForKey:@"url"] == nil) 106 [NSException raise:@"SUAppcastException" format:@"Couldn't find an download URL for feed entry %@!", [self title]]; 107 [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; 108 [self setDSASignature:[enclosure objectForKey:@"sparkle:dsaSignature"]]; 109 110 // Try to find a version string. 111 // Finding the new version number from the RSS feed is a little bit hacky. There are two ways: 112 // 1. A "sparkle:version" attribute on the enclosure tag, an extension from the RSS spec. 113 // 2. If there isn't a version attribute, Sparkle will parse the path in the enclosure, expecting 114 // that it will look like this: http://something.com/YourApp_0.5.zip. It'll read whatever's between the last 115 // underscore and the last period as the version number. So name your packages like this: APPNAME_VERSION.extension. 116 // The big caveat with this is that you can't have underscores in your version strings, as that'll confuse Sparkle. 117 // Feel free to change the separator string to a hyphen or something more suited to your needs if you like. 118 NSString *newVersion = [enclosure objectForKey:@"sparkle:version"]; 119 if (!newVersion) // no sparkle:version attribute 120 { 121 // Separate the url by underscores and take the last component, as that'll be closest to the end, 122 // then we remove the extension. Hopefully, this will be the version. 123 NSArray *fileComponents = [[enclosure objectForKey:@"url"] componentsSeparatedByString:@"_"]; 124 if ([fileComponents count] > 1) 125 newVersion = [[fileComponents lastObject] stringByDeletingPathExtension]; 126 else 127 [NSException raise:@"SUAppcastException" format:@"Couldn't find a version string for %@! You need a sparkle:version attribute.", [enclosure objectForKey:@"url"]]; 128 } 129 [self setVersionString:newVersion]; 130 [self setMinimumSystemVersion:[dict objectForKey:@"sparkle:minimumSystemVersion"]]; 131 132 NSString *shortVersionString = [enclosure objectForKey:@"sparkle:shortVersionString"]; 133 if (shortVersionString) 134 [self setDisplayVersionString:shortVersionString]; 135 else 136 [self setDisplayVersionString:[self versionString]]; 137 138 // Find the appropriate release notes URL. 139 if ([dict objectForKey:@"sparkle:releaseNotesLink"]) 140 [self setReleaseNotesURL:[NSURL URLWithString:[dict objectForKey:@"sparkle:releaseNotesLink"]]]; 141 else if ([[self description] hasPrefix:@"http://"]) // if the description starts with http://, use that. 142 [self setReleaseNotesURL:[NSURL URLWithString:[self description]]]; 143 else 144 [self setReleaseNotesURL:nil]; 145 } 146 return self; 147 } 168 148 169 149 - (void)dealloc … … 174 154 [self setReleaseNotesURL:nil]; 175 155 [self setDSASignature:nil]; 176 [self setMD5Sum:nil];177 156 [self setFileURL:nil]; 178 [self set FileVersion:nil];179 [self set VersionString:nil];157 [self setVersionString:nil]; 158 [self setDisplayVersionString:nil]; 180 159 [super dealloc]; 181 160 } trunk/SUAutomaticUpdateAlert.h
r125 r153 12 12 #import "SUWindowController.h" 13 13 14 typedef enum 15 { 16 SUInstallNowChoice, 17 SUInstallLaterChoice, 18 SUDoNotInstallChoice 19 } SUAutomaticInstallationChoice; 20 14 21 @class SUAppcastItem; 15 22 @interface SUAutomaticUpdateAlert : SUWindowController { 16 23 SUAppcastItem *updateItem; 24 id delegate; 17 25 NSBundle *hostBundle; 18 26 } 19 27 20 - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle ;21 22 - (IBAction) relaunchNow:sender;23 - (IBAction) relaunchLater:sender;28 - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hostBundle delegate:delegate; 29 - (IBAction)installNow:sender; 30 - (IBAction)installLater:sender; 31 - (IBAction)doNotInstall:sender; 24 32 25 33 @end 26 34 35 @interface NSObject (SUAutomaticUpdateAlertDelegateProtocol) 36 - (void)automaticUpdateAlert:(SUAutomaticUpdateAlert *)aua finishedWithChoice:(SUAutomaticInstallationChoice)choice; 37 @end 38 27 39 #endif trunk/SUAutomaticUpdateAlert.m
r132 r153 12 12 @implementation SUAutomaticUpdateAlert 13 13 14 - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb ;14 - (id)initWithAppcastItem:(SUAppcastItem *)item hostBundle:(NSBundle *)hb delegate:del; 15 15 { 16 16 self = [super initWithHostBundle:hb windowNibName:@"SUAutomaticUpdateAlert"]; … … 18 18 { 19 19 updateItem = [item retain]; 20 delegate = del; 20 21 hostBundle = [hb retain]; 21 22 [self setShouldCascadeWindows:NO]; 23 [[self window] center]; 22 24 } 23 25 return self; 24 26 } 25 27 26 - (void) dealloc28 - (void)dealloc 27 29 { 28 30 [hostBundle release]; … … 32 34 33 35 34 - (IBAction) relaunchNow:sender36 - (IBAction)installNow:sender 35 37 { 36 38 [self close]; 37 [ NSApp stopModalWithCode:NSAlertDefaultReturn];39 [delegate automaticUpdateAlert:self finishedWithChoice:SUInstallNowChoice]; 38 40 } 39 41 40 - (IBAction) relaunchLater:sender42 - (IBAction)installLater:sender 41 43 { 42 44 [self close]; 43 [NSApp stopModalWithCode:NSAlertAlternateReturn]; 45 [delegate automaticUpdateAlert:self finishedWithChoice:SUInstallLaterChoice]; 46 } 47 48 - (IBAction)doNotInstall:sender 49 { 50 [self close]; 51 [delegate automaticUpdateAlert:self finishedWithChoice:SUDoNotInstallChoice]; 44 52 } 45 53 … … 51 59 - (NSString *)titleText 52 60 { 53 return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ has been installed!", nil), [hostBundle name]];61 return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is ready to install!", nil), [hostBundle name]]; 54 62 } 55 63 56 64 - (NSString *)descriptionText 57 65 { 58 return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been installed and will be ready to use next time %1$@ starts! Would you like to relaunchnow?", nil), [hostBundle name], [hostBundle displayVersion]];66 return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?", nil), [hostBundle name], [hostBundle displayVersion]]; 59 67 } 60 68 trunk/SUConstants.h
r147 r153 13 13 14 14 extern NSString *SUUpdaterWillRestartNotification; 15 extern NSString *SUTechnicalErrorInformationKey; 15 16 16 17 extern NSString *SUFeedURLKey; … … 29 30 30 31 extern NSString *SUSparkleErrorDomain; 32 // Appcast phase errors. 33 extern OSStatus SUAppcastParseError; 34 extern OSStatus SUNoUpdateError; 35 extern OSStatus SUAppcastError; 36 extern OSStatus SURunningFromDiskImageError; 37 38 // Downlaod phase errors. 39 extern OSStatus SUTemporaryDirectoryError; 40 41 // Extraction phase errors. 42 extern OSStatus SUUnarchivingError; 43 extern OSStatus SUSignatureError; 44 45 // Installation phase errors. 31 46 extern OSStatus SUFileCopyFailure; 32 47 extern OSStatus SUAuthenticationFailure; 33 48 extern OSStatus SUMissingUpdateError; 34 49 extern OSStatus SUMissingInstallerToolError; 50 extern OSStatus SURelaunchError; 51 extern OSStatus SUInstallationError; 35 52 36 53 // NSInteger is a type that was added to Leopard. trunk/SUConstants.m
r147 r153 11 11 12 12 NSString *SUUpdaterWillRestartNotification = @"SUUpdaterWillRestartNotificationName"; 13 NSString *SUTechnicalErrorInformationKey = @"SUTechnicalErrorInformation"; 13 14 14 15 NSString *SUHasLaunchedBeforeKey = @"SUHasLaunchedBefore"; … … 27 28 28 29 NSString *SUSparkleErrorDomain = @"SUSparkleErrorDomain"; 29 OSStatus SUFileCopyFailure = 9000; 30 OSStatus SUAuthenticationFailure = 9001; 31 OSStatus SUMissingUpdateError = 9002; 32 OSStatus SUMissingInstallerToolError = 9003; 30 OSStatus SUAppcastParseError = 1000; 31 OSStatus SUNoUpdateError = 1001; 32 OSStatus SUAppcastError = 1002; 33 OSStatus SURunningFromDiskImageError = 1003; 34 35 OSStatus SUTemporaryDirectoryError = 2000; 36 37 OSStatus SUUnarchivingError = 3000; 38 OSStatus SUSignatureError = 3001; 39 40 OSStatus SUFileCopyFailure = 4000; 41 OSStatus SUAuthenticationFailure = 4001; 42 OSStatus SUMissingUpdateError = 4002; 43 OSStatus SUMissingInstallerToolError = 4003; 44 OSStatus SURelaunchError = 4004; 45 OSStatus SUInstallationError = 4005; trunk/SUInstaller.h
r147 r153 7 7 // 8 8 9 #ifndef SUINSTALLER_H 10 #define SUINSTALLER_H 11 9 12 #import <Cocoa/Cocoa.h> 10 13 11 14 @interface SUInstaller : NSObject { } 12 + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate ;15 + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously; 13 16 + (void)_finishInstallationWithResult:(BOOL)result hostBundle:(NSBundle *)hostBundle error:(NSError *)error delegate:delegate; 14 17 @end 15 18 16 19 @interface NSObject (SUInstallerDelegateInformalProtocol) 17 - installerFinishedForHostBundle:(NSBundle *)hostBundle;18 - installerForHostBundle:(NSBundle *)hostBundle failedWithError:(NSError *)error;20 - (void)installerFinishedForHostBundle:(NSBundle *)hostBundle; 21 - (void)installerForHostBundle:(NSBundle *)hostBundle failedWithError:(NSError *)error; 19 22 @end 20 23 21 extern NSString *SUInstallerPathKey; 22 extern NSString *SUInstallerHostBundleKey; 23 extern NSString *SUInstallerDelegateKey; 24 #endif trunk/SUInstaller.m
r152 r153 17 17 @implementation SUInstaller 18 18 19 + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate ;19 + (void)installFromUpdateFolder:(NSString *)updateFolder overHostBundle:(NSBundle *)hostBundle delegate:delegate synchronously:(BOOL)synchronously; 20 20 { 21 21 // Search subdirectories for the application … … 57 57 else 58 58 { 59 NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:newAppDownloadPath, SUInstallerPathKey, hostBundle, SUInstallerHostBundleKey, delegate, SUInstallerDelegateKey, nil]; 60 [NSThread detachNewThreadSelector:@selector(performInstallationWithInfo:) toTarget:(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) withObject:info]; 59