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