]> Dogcows Code - chaz/thecheat/blob - ChazUpdate.m
The Cheat 1.2.1
[chaz/thecheat] / ChazUpdate.m
1
2 // **********************************************************************
3 // The Cheat - A universal game cheater for Mac OS X
4 // (C) 2003-2005 Chaz McGarvey (BrokenZipper)
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 1, or (at your option)
9 // any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 //
20
21
22 #import "ChazUpdate.h"
23
24
25 // an NSError that an have an error statement associated with it
26 @interface ChazUpdateError : NSError
27 {
28 NSString *_errorString;
29 }
30 + (id)errorWithString:(NSString *)string;
31 - (id)initWithString:(NSString *)string;
32 - (void)setErrorString:(NSString *)string;
33 - (NSString *)localizedDescription;
34 @end
35 @implementation ChazUpdateError
36 + (id)errorWithString:(NSString *)string
37 {
38 return [[[ChazUpdateError alloc] initWithString:string] autorelease];
39 }
40 - (id)initWithString:(NSString *)string
41 {
42 if ( self = [super initWithDomain:@"ChazUpdateError" code:1 userInfo:nil] ) {
43 [self setErrorString:string];
44 }
45 return self;
46 }
47 - (void)setErrorString:(NSString *)string
48 {
49 [string retain];
50 [_errorString release];
51 _errorString = string;
52 }
53 - (NSString *)localizedDescription
54 {
55 return _errorString;
56 }
57 @end
58
59
60 @interface ChazUpdate : NSObject
61 {
62 NSString *_name;
63
64 NSURLConnection *_connection;
65 NSHTTPURLResponse *_response;
66 NSMutableData *_responseData;
67
68 BOOL _verbose;
69 }
70
71 - (id)initWithURL:(id)url name:(NSString *)name verbose:(BOOL)verbose;
72 - (void)kill;
73
74 @end
75
76
77 @implementation ChazUpdate
78
79
80 #pragma mark Initialization
81
82 - (id)initWithURL:(id)url name:(NSString *)name verbose:(BOOL)verbose
83 {
84 NSURL *theURL;
85
86 if ( self = [super init] )
87 {
88 // figure out what type of URL was passed
89 if ( [url isKindOfClass:[NSURL class]] ) {
90 theURL = url;
91 }
92 else if ( [url isKindOfClass:[NSString class]] ) {
93 theURL = [[[NSURL alloc] initWithString:url] autorelease];
94 }
95 else {
96 if ( verbose ) {
97 NSRunAlertPanel( @"Update Check Failed",
98 @"Could not determine the location of the version information file.", @"OK", nil, nil );
99 }
100 [self release];
101 return nil;
102 }
103 // save the name
104 if ( name ) {
105 _name = [name retain];
106 }
107 else {
108 _name = [[NSString stringWithString:@""] retain];
109 }
110 _verbose = verbose;
111
112 // create the request
113 _connection = [[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:theURL
114 cachePolicy:NSURLRequestReloadIgnoringCacheData
115 timeoutInterval:30.0] delegate:self] retain];
116 _response = nil;
117 _responseData = [[NSMutableData alloc] init];
118 }
119 return self;
120 }
121
122
123 - (void)dealloc
124 {
125 // cleanup
126 [_name release];
127
128 [_connection release];
129 [_response release];
130 [_responseData release];
131
132 [super dealloc];
133 }
134
135 - (void)kill
136 {
137 [self release];
138 self = nil;
139 }
140
141
142 #pragma mark NSURLConnection Delegate
143
144 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
145 {
146 if ( _response ) {
147 // another response, chop the data
148 [_responseData setLength:0];
149 [_response release];
150 }
151 _response = [response retain];
152 }
153
154 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
155 {
156 // just save the data
157 [_responseData appendData:data];
158 }
159
160 -(void)connectionDidFinishLoading:(NSURLConnection *)connection
161 {
162 NSDictionary *plistDictionary;
163 NSPropertyListFormat plistFormat;
164 NSString *plistError;
165
166 NSDictionary *appDictionary;
167
168 NSString *appRedirect;
169 NSString *appVersion;
170 NSString *appOnline;
171 NSDate *appReleaseDate;
172 NSString *appDownload;
173 NSString *appDescription;
174
175 // read in the recently downloaded plist
176 plistDictionary = [NSPropertyListSerialization propertyListFromData:_responseData
177 mutabilityOption:NSPropertyListImmutable
178 format:&plistFormat
179 errorDescription:&plistError];
180 if ( !plistDictionary ) {
181 [self connection:connection didFailWithError:[ChazUpdateError errorWithString:plistError]];
182 return;
183 }
184 // make sure the root object is a dictionary
185 if ( ![NSStringFromClass([plistDictionary class]) isEqualToString:@"NSCFDictionary"] ) {
186 [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"the version information file is ill-formatted"]];
187 return;
188 }
189 appDictionary = [plistDictionary objectForKey:_name];
190 if ( !appDictionary ) {
191 [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"no version information for this application"]];
192 return;
193 }
194
195 // see if the version information for this app is in another file
196 if ( appRedirect = [appDictionary objectForKey:@"Redirect"] ) {
197 // recursively follow the redirection
198 [[ChazUpdate alloc] initWithURL:appRedirect name:_name verbose:_verbose];
199 [self kill];
200 return;
201 }
202
203 // read other useful information about the app
204 appVersion = [appDictionary objectForKey:@"Version"];
205 appReleaseDate = [NSDate dateWithNaturalLanguageString:[appDictionary objectForKey:@"ReleaseDate"]];
206 appOnline = [appDictionary objectForKey:@"MoreInfoURL"];
207 appDownload = [appDictionary objectForKey:@"DownloadURL"];
208 appDescription = [appDictionary objectForKey:@"Description"];
209
210 if ( !appReleaseDate ) {
211 [self connection:connection didFailWithError:[ChazUpdateError errorWithString:@"missing version information"]];
212 return;
213 }
214
215 if ( !appVersion ) {
216 appVersion = @"";
217 }
218
219 if ( [ChazAppBuildDate() compare:appReleaseDate] == NSOrderedAscending ) {
220 // there is a new version, display message
221 int choice;
222
223 NSString *desc;
224 NSString *mainBtn;
225 NSString *alternate;
226 NSString *other;
227
228 if ( appDescription ) {
229 desc = appDescription;
230 }
231 else {
232 desc = [NSString stringWithFormat:@"A new version of %@ is available! Would you like to learn more about the new version now?", _name];
233 }
234 if ( appOnline ) {
235 mainBtn = @"Learn More";
236 other = @"Cancel";
237 }
238 else {
239 mainBtn = @"OK";
240 other = nil;
241 }
242 if ( appDownload ) {
243 alternate = @"Download";
244 }
245 else {
246 alternate = nil;
247 }
248
249 choice = NSRunInformationalAlertPanel( [NSString stringWithFormat:@"New Version: %@ %@", _name, appVersion],
250 desc, mainBtn, alternate, other );
251
252 if ( choice == NSAlertDefaultReturn && appOnline ) {
253 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:appOnline]];
254 }
255 else if ( choice == NSAlertAlternateReturn ) {
256 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:appDownload]];
257 }
258 }
259 else if ( _verbose ) {
260 // alert that the user has the latest version if we're in verbose mode
261 NSRunInformationalAlertPanel( [NSString stringWithFormat:@"%@ %@", _name, ChazAppVersion()],
262 @"You have the latest version of %@.", @"OK", nil, nil, _name );
263 }
264
265 [self kill];
266 }
267
268 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
269 {
270 if ( _verbose )
271 {
272 // check failed, display an error
273 NSRunAlertPanel( @"Update Check Failed", @"Check could not get new version data.\nError code #%i (%@)", @"OK", nil, nil, [error code], [error localizedDescription] );
274 }
275
276 [self kill];
277 }
278
279 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
280 {
281 // handle http redirection
282 return request;
283 }
284
285
286 @end
287
288
289 #pragma mark API Functions
290
291 void ChazCheckForUpdate( id url, BOOL userRequested )
292 {
293 [[ChazUpdate alloc] initWithURL:url name:ChazAppName() verbose:userRequested];
294 }
295
296 void ChazCheckForUpdateWithName( id url, NSString *appName, BOOL userRequested )
297 {
298 [[ChazUpdate alloc] initWithURL:url name:appName verbose:userRequested];
299 }
300
301
302 NSString *ChazAppName()
303 {
304 NSString *name;
305
306 name = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"];
307 if ( name && ![name isEqualToString:@""] ) {
308 return name;
309 }
310 name = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
311 if ( name && ![name isEqualToString:@""] ) {
312 return name;
313 }
314 return @"";
315 }
316
317 NSString *ChazAppVersion()
318 {
319 NSString *vers;
320
321 vers = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
322 if ( vers && ![vers isEqualToString:@""] ) {
323 return vers;
324 }
325 vers = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
326 if ( vers && ![vers isEqualToString:@""] ) {
327 return vers;
328 }
329 return @"";
330 }
331
332 NSDate *ChazAppBuildDate()
333 {
334 return [NSDate dateWithNaturalLanguageString:[NSString stringWithFormat:@"%s %s", __TIME__, __DATE__]];
335 }
336
This page took 0.045114 seconds and 4 git commands to generate.