]> Dogcows Code - chaz/thecheat/blobdiff - ChazUpdate.m
The Cheat 1.2
[chaz/thecheat] / ChazUpdate.m
diff --git a/ChazUpdate.m b/ChazUpdate.m
new file mode 100644 (file)
index 0000000..33de369
--- /dev/null
@@ -0,0 +1,323 @@
+//
+//  ChazUpdate.m
+//  The Cheat
+//
+//  Created by Chaz McGarvey on 1/5/05.
+//  Copyright 2005 Chaz McGarvey. All rights reserved.
+//
+
+#import "ChazUpdate.h"
+
+
+// an NSError that an have an error statement associated with it
+@interface ChazUpdateError : NSError
+{
+       NSString *_errorString;
+}
++ (id)errorWithString:(NSString *)string;
+- (id)initWithString:(NSString *)string;
+- (void)setErrorString:(NSString *)string;
+- (NSString *)localizedDescription;
+@end
+@implementation ChazUpdateError
++ (id)errorWithString:(NSString *)string
+{
+       return [[[ChazUpdateError alloc] initWithString:string] autorelease];
+}
+- (id)initWithString:(NSString *)string
+{
+       if ( self = [super initWithDomain:@"ChazUpdateError" code:1 userInfo:nil] ) {
+               [self setErrorString:string];
+       }
+       return self;
+}
+- (void)setErrorString:(NSString *)string
+{
+       [string retain];
+       [_errorString release];
+       _errorString = string;
+}
+- (NSString *)localizedDescription
+{
+       return _errorString;
+}
+@end
+
+
+@interface ChazUpdate : NSObject
+{
+       NSString *_name;
+       
+       NSURLConnection *_connection;
+       NSHTTPURLResponse *_response;
+       NSMutableData *_responseData;
+       
+       BOOL _verbose;
+}
+
+- (id)initWithURL:(id)url name:(NSString *)name verbose:(BOOL)verbose;
+- (void)kill;
+
+@end
+
+
+@implementation ChazUpdate
+
+
+#pragma mark Initialization
+
+- (id)initWithURL:(id)url name:(NSString *)name verbose:(BOOL)verbose
+{
+       NSURL *theURL;
+       
+       if ( self = [super init] )
+       {
+               // figure out what type of URL was passed
+               if ( [url isKindOfClass:[NSURL class]] ) {
+                       theURL = url;
+               }
+               else if ( [url isKindOfClass:[NSString class]] ) {
+                       theURL = [[[NSURL alloc] initWithString:url] autorelease];
+               }
+               else {
+                       if ( verbose ) {
+                               NSRunAlertPanel( @"Update Check Failed",
+                                                                @"Could not determine the location of the version information file.", @"OK", nil, nil );
+                       }
+                       [self release];
+                       return nil;
+               }
+               // save the name
+               if ( name ) {
+                       _name = [name retain];
+               }
+               else {
+                       _name = [[NSString stringWithString:@""] retain];
+               }
+               _verbose = verbose;
+               
+               // create the request
+               _connection = [[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:theURL
+                                                                                                                                                          cachePolicy:NSURLRequestReloadIgnoringCacheData
+                                                                                                                                                  timeoutInterval:30.0] delegate:self] retain];
+               _response = nil;
+               _responseData = [[NSMutableData alloc] init];
+       }
+       return self;
+}
+
+
+- (void)dealloc
+{
+       // cleanup
+       [_name release];
+       
+       [_connection release];
+       [_response release];
+       [_responseData release];
+       
+       [super dealloc];
+}
+
+- (void)kill
+{
+       [self release];
+       self = nil;
+}
+
+
+#pragma mark NSURLConnection Delegate
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
+{
+       if ( _response ) {
+               // another response, chop the data
+               [_responseData setLength:0];
+               [_response release];
+       }
+       _response = [response retain];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+{
+       // just save the data
+       [_responseData appendData:data];
+}
+
+-(void)connectionDidFinishLoading:(NSURLConnection *)connection
+{
+       NSDictionary *plistDictionary;
+       NSPropertyListFormat plistFormat;
+       NSString *plistError;
+       
+       NSDictionary *appDictionary;
+       
+       NSString *appRedirect;
+       NSString *appVersion;
+       NSString *appOnline;
+       NSDate *appReleaseDate;
+       NSString *appDownload;
+       NSString *appDescription;
+       
+       // read in the recently downloaded plist
+       plistDictionary = [NSPropertyListSerialization propertyListFromData:_responseData
+                                                                                                          mutabilityOption:NSPropertyListImmutable
+                                                                                                                                format:&plistFormat
+                                                                                                          errorDescription:&plistError];
+       if ( !plistDictionary ) {
+               [self connection:connection didFailWithError:[ChazUpdateError errorWithString:plistError]];
+               return;
+       }
+       // make sure the root object is a dictionary
+       if ( ![NSStringFromClass([plistDictionary class]) isEqualToString:@"NSCFDictionary"] ) {
+               [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"the version information file is ill-formatted"]];
+               return;
+       }
+       appDictionary = [plistDictionary objectForKey:_name];
+       if ( !appDictionary ) {
+               [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"no version information for this application"]];
+               return;
+       }
+       
+       // see if the version information for this app is in another file
+       if ( appRedirect = [appDictionary objectForKey:@"Redirect"] ) {
+               // recursively follow the redirection
+               [[ChazUpdate alloc] initWithURL:appRedirect name:_name verbose:_verbose];
+               [self kill];
+               return;
+       }
+       
+       // read other useful information about the app
+       appVersion = [appDictionary objectForKey:@"Version"];
+       appReleaseDate = [NSDate dateWithNaturalLanguageString:[appDictionary objectForKey:@"ReleaseDate"]];
+       appOnline = [appDictionary objectForKey:@"MoreInfoURL"];
+       appDownload = [appDictionary objectForKey:@"DownloadURL"];
+       appDescription = [appDictionary objectForKey:@"Description"];
+       
+       if ( !appReleaseDate ) {
+               [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"missing version information"]];
+               return;
+       }
+       
+       if ( !appVersion ) {
+               appVersion = @"";
+       }
+       
+       if ( [ChazAppBuildDate() compare:appReleaseDate] == NSOrderedAscending ) {
+               // there is a new version, display message
+               int choice;
+               
+               NSString *desc;
+               NSString *mainBtn;
+               NSString *alternate;
+               NSString *other;
+               
+               if ( appDescription ) {
+                       desc = appDescription;
+               }
+               else {
+                       desc = [NSString stringWithFormat:@"A new version of %@ is available!  Would you like to learn more about the new version now?", _name];
+               }
+               if ( appOnline ) {
+                       mainBtn = @"Learn More";
+                       other = @"Cancel";
+               }
+               else {
+                       mainBtn = @"OK";
+                       other = nil;
+               }
+               if ( appDownload ) {
+                       alternate = @"Download";
+               }
+               else {
+                       alternate = nil;
+               }
+               
+               choice = NSRunInformationalAlertPanel( [NSString stringWithFormat:@"New Version: %@ %@", _name, appVersion],
+                                                                                          desc, mainBtn, alternate, other );
+               
+               if ( choice == NSAlertDefaultReturn && appOnline ) {
+                       [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:appOnline]];
+               }
+               else if ( choice == NSAlertAlternateReturn ) {
+                       [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:appDownload]];
+               }
+       }
+       else if ( _verbose ) {
+               // alert that the user has the latest version if we're in verbose mode
+               NSRunInformationalAlertPanel( [NSString stringWithFormat:@"%@ %@", _name, ChazAppVersion()],
+                                                                         @"You have the latest version of %@.", @"OK", nil, nil, _name );
+       }
+       
+       [self kill];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
+{
+       if ( _verbose )
+       {
+               // check failed, display an error
+               NSRunAlertPanel( @"Update Check Failed", @"Check could not get new version data.\nError code #%i (%@)", @"OK", nil, nil, [error code], [error localizedDescription] );
+       }
+       
+       [self kill];
+}
+
+- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
+{
+       // handle http redirection
+       return request;
+}
+
+
+@end
+
+
+#pragma mark API Functions
+
+void ChazCheckForUpdate( id url, BOOL userRequested )
+{
+       [[ChazUpdate alloc] initWithURL:url name:ChazAppName() verbose:userRequested];
+}
+
+void ChazCheckForUpdateWithName( id url, NSString *appName, BOOL userRequested )
+{
+       [[ChazUpdate alloc] initWithURL:url name:appName verbose:userRequested];
+}
+
+
+NSString *ChazAppName()
+{
+       NSString *name;
+       
+       name = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"];
+       if ( name && ![name isEqualToString:@""] ) {
+               return name;
+       }
+       name = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+       if ( name && ![name isEqualToString:@""] ) {
+               return name;
+       }
+       return @"";
+}
+
+NSString *ChazAppVersion()
+{
+       NSString *vers;
+       
+       vers = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
+       if ( vers && ![vers isEqualToString:@""] ) {
+               return vers;
+       }
+       vers = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
+       if ( vers && ![vers isEqualToString:@""] ) {
+               return vers;
+       }
+       return @"";
+}
+
+NSDate *ChazAppBuildDate()
+{
+       return [NSDate dateWithNaturalLanguageString:[NSString stringWithFormat:@"%s %s", __TIME__, __DATE__]];
+}
+
This page took 0.021975 seconds and 4 git commands to generate.