3 * The Cheat - The legendary universal game trainer for Mac OS X.
4 * http://www.brokenzipper.com/trac/wiki/TheCheat
6 * Copyright (c) 2003-2011, Charles McGarvey et al.
8 * Distributable under the terms and conditions of the 2-clause BSD
9 * license; see the file COPYING for the legal text of the license.
13 #import "ChazUpdate.h"
16 // an NSError that an have an error statement associated with it
17 @interface ChazUpdateError
: NSError
19 NSString
*_errorString
;
21 + (id)errorWithString
:(NSString
*)string
;
22 - (id)initWithString
:(NSString
*)string
;
23 - (void)setErrorString
:(NSString
*)string
;
24 - (NSString
*)localizedDescription
;
26 @implementation ChazUpdateError
27 + (id)errorWithString
:(NSString
*)string
29 return [[[ChazUpdateError alloc
] initWithString
:string
] autorelease
];
31 - (id)initWithString
:(NSString
*)string
33 if ( self = [super initWithDomain
:@
"ChazUpdateError" code
:1 userInfo
:nil] ) {
34 [self setErrorString
:string
];
38 - (void)setErrorString
:(NSString
*)string
41 [_errorString release
];
42 _errorString
= string
;
44 - (NSString
*)localizedDescription
51 @interface ChazUpdate
: NSObject
55 NSURLConnection
*_connection
;
56 NSHTTPURLResponse
*_response
;
57 NSMutableData
*_responseData
;
62 - (id)initWithURL
:(id)url name
:(NSString
*)name verbose
:(BOOL)verbose
;
68 @implementation ChazUpdate
71 #pragma mark Initialization
73 - (id)initWithURL
:(id)url name
:(NSString
*)name verbose
:(BOOL)verbose
77 if ( self = [super init
] )
79 // figure out what type of URL was passed
80 if ( [url isKindOfClass
:[NSURL
class]] ) {
83 else if ( [url isKindOfClass
:[NSString
class]] ) {
84 theURL
= [[[NSURL alloc
] initWithString
:url
] autorelease
];
88 NSRunAlertPanel( @
"Update Check Failed",
89 @
"Could not determine the location of the version information file.", @
"OK", nil, nil );
96 _name
= [name retain
];
99 _name
= [[NSString stringWithString
:@
""] retain
];
103 // create the request
104 _connection
= [[NSURLConnection connectionWithRequest
:[NSURLRequest requestWithURL
:theURL
105 cachePolicy
:NSURLRequestReloadIgnoringCacheData
106 timeoutInterval
:30.0] delegate
:self] retain
];
108 _responseData
= [[NSMutableData alloc
] init
];
119 [_connection release
];
121 [_responseData release
];
133 #pragma mark NSURLConnection Delegate
135 - (void)connection
:(NSURLConnection
*)connection didReceiveResponse
:(NSURLResponse
*)response
138 // another response, chop the data
139 [_responseData setLength
:0];
142 _response
= [response retain
];
145 - (void)connection
:(NSURLConnection
*)connection didReceiveData
:(NSData
*)data
147 // just save the data
148 [_responseData appendData
:data
];
151 -(void)connectionDidFinishLoading
:(NSURLConnection
*)connection
153 NSDictionary
*plistDictionary
;
154 NSPropertyListFormat plistFormat
;
155 NSString
*plistError
;
157 NSDictionary
*appDictionary
;
159 NSString
*appRedirect
;
160 NSString
*appVersion
;
162 NSDate
*appReleaseDate
;
163 NSString
*appDownload
;
164 NSString
*appDescription
;
166 // read in the recently downloaded plist
167 plistDictionary
= [NSPropertyListSerialization propertyListFromData
:_responseData
168 mutabilityOption
:NSPropertyListImmutable
170 errorDescription
:&plistError
];
171 if ( !plistDictionary
) {
172 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:plistError
]];
175 // make sure the root object is a dictionary
176 if ( ![NSStringFromClass([plistDictionary
class]) isEqualToString
:@
"NSCFDictionary"] ) {
177 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"the version information file is ill-formatted"]];
180 appDictionary
= [plistDictionary objectForKey
:_name
];
181 if ( !appDictionary
) {
182 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"no version information for this application"]];
186 // see if the version information for this app is in another file
187 if ( (appRedirect
= [appDictionary objectForKey
:@
"Redirect"]) ) {
188 // recursively follow the redirection
189 [[ChazUpdate alloc
] initWithURL
:appRedirect name
:_name verbose
:_verbose
];
194 // read other useful information about the app
195 appVersion
= [appDictionary objectForKey
:@
"Version"];
196 appReleaseDate
= [NSDate dateWithNaturalLanguageString
:[appDictionary objectForKey
:@
"ReleaseDate"]];
197 appOnline
= [appDictionary objectForKey
:@
"MoreInfoURL"];
198 appDownload
= [appDictionary objectForKey
:@
"DownloadURL"];
199 appDescription
= [appDictionary objectForKey
:@
"Description"];
201 if ( !appReleaseDate
) {
202 [self connection
:connection didFailWithError
:[ChazUpdateError errorWithString
:@
"missing version information"]];
210 if ( [ChazAppBuildDate() compare
:appReleaseDate
] == NSOrderedAscending
) {
211 // there is a new version, display message
219 if ( appDescription
) {
220 desc
= appDescription
;
223 desc
= [NSString stringWithFormat
:@
"A new version of %@ is available! Would you like to learn more about the new version now?", _name
];
226 mainBtn
= @
"Learn More";
234 alternate
= @
"Download";
240 choice
= NSRunInformationalAlertPanel( [NSString stringWithFormat
:@
"New Version: %@ %@", _name
, appVersion
],
241 desc
, mainBtn
, alternate
, other
);
243 if ( choice
== NSAlertDefaultReturn
&& appOnline
) {
244 [[NSWorkspace sharedWorkspace
] openURL
:[NSURL URLWithString
:appOnline
]];
246 else if ( choice
== NSAlertAlternateReturn
) {
247 [[NSWorkspace sharedWorkspace
] openURL
:[NSURL URLWithString
:appDownload
]];
250 else if ( _verbose
) {
251 // alert that the user has the latest version if we're in verbose mode
252 NSRunInformationalAlertPanel( [NSString stringWithFormat
:@
"%@ %@", _name
, ChazAppVersion()],
253 @
"You have the latest version of %@.", @
"OK", nil, nil, _name
);
259 - (void)connection
:(NSURLConnection
*)connection didFailWithError
:(NSError
*)error
263 // check failed, display an error
264 NSRunAlertPanel( @
"Update Check Failed", @
"Check could not get new version data.\nError code #%i (%@)", @
"OK", nil, nil, [error code
], [error localizedDescription
] );
270 - (NSURLRequest
*)connection
:(NSURLConnection
*)connection willSendRequest
:(NSURLRequest
*)request redirectResponse
:(NSURLResponse
*)redirectResponse
272 // handle http redirection
280 #pragma mark API Functions
282 void ChazCheckForUpdate( id url
, BOOL userRequested
)
284 [[ChazUpdate alloc
] initWithURL
:url name
:ChazAppName() verbose
:userRequested
];
287 void ChazCheckForUpdateWithName( id url
, NSString
*appName
, BOOL userRequested
)
289 [[ChazUpdate alloc
] initWithURL
:url name
:appName verbose
:userRequested
];
293 NSString
*ChazAppName()
297 name
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleExecutable"];
298 if ( name
&& ![name isEqualToString
:@
""] ) {
301 name
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleName"];
302 if ( name
&& ![name isEqualToString
:@
""] ) {
308 NSString
*ChazAppVersion()
312 vers
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleShortVersionString"];
313 if ( vers
&& ![vers isEqualToString
:@
""] ) {
316 vers
= [[[NSBundle mainBundle
] infoDictionary
] objectForKey
:@
"CFBundleVersion"];
317 if ( vers
&& ![vers isEqualToString
:@
""] ) {
323 NSDate
*ChazAppBuildDate()
325 return [NSDate dateWithNaturalLanguageString
:[NSString stringWithFormat
:@
"%s %s", __TIME__
, __DATE__
]];