Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 282 lines (247 sloc) 8.754 kB
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
1 //
2 // SUAppcast.m
3 // Sparkle
4 //
5 // Created by Andy Matuschak on 3/12/06.
6 // Copyright 2006 Andy Matuschak. All rights reserved.
7 //
8
5aa1e18 Improved headers from Charles D. H. Williams.
andym authored
9 #import "Sparkle.h"
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
10 #import "SUAppcast.h"
11
6e648f1 Add support for parsing an extended <sparkle:deltas> element in the a…
Mark Rowe authored
12 @interface NSXMLElement (SUAppcastExtensions)
13 - (NSDictionary *)attributesAsDictionary;
14 @end
15
16 @implementation NSXMLElement (SUAppcastExtensions)
17 - (NSDictionary *)attributesAsDictionary
18 {
19 NSEnumerator *attributeEnum = [[self attributes] objectEnumerator];
20 NSXMLNode *attribute;
21 NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
22
23 while ((attribute = [attributeEnum nextObject]))
24 [dictionary setObject:[attribute stringValue] forKey:[attribute name]];
25 return dictionary;
26 }
27 @end
28
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
29 @interface SUAppcast (Private)
30 - (void)reportError:(NSError *)error;
a148504 @andymatuschak Fixes 252986
andymatuschak authored
31 - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
32 @end
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
33
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
34 @implementation SUAppcast
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
35
36 - (void)dealloc
37 {
38 [items release];
46312a2 @andymatuschak Fixes 258060
andymatuschak authored
39 [userAgentString release];
2e3ed79 @andymatuschak Fixing #502212: clang static analyzer identifies 2 leaks in Sparkle code
andymatuschak authored
40 [download release];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
41 [super dealloc];
42 }
43
44 - (NSArray *)items
45 {
46 return items;
47 }
48
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
49 - (void)fetchAppcastFromURL:(NSURL *)url
50 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
51 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
52 if (userAgentString)
53 [request setValue:userAgentString forHTTPHeaderField:@"User-Agent"];
54
2e3ed79 @andymatuschak Fixing #502212: clang static analyzer identifies 2 leaks in Sparkle code
andymatuschak authored
55 download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
56 }
57
2e3ed79 @andymatuschak Fixing #502212: clang static analyzer identifies 2 leaks in Sparkle code
andymatuschak authored
58 - (void)download:(NSURLDownload *)aDownload decideDestinationWithSuggestedFilename:(NSString *)filename
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
59 {
d7774c0 performed a code review, specifically:
Sean McBride authored
60 NSString* destinationFilename = NSTemporaryDirectory();
61 if (destinationFilename)
62 {
63 destinationFilename = [destinationFilename stringByAppendingPathComponent:filename];
64 [download setDestination:destinationFilename allowOverwrite:NO];
65 }
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
66 }
67
2e3ed79 @andymatuschak Fixing #502212: clang static analyzer identifies 2 leaks in Sparkle code
andymatuschak authored
68 - (void)download:(NSURLDownload *)aDownload didCreateDestination:(NSString *)path
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
69 {
9d260f9 @andymatuschak Bug #275309: Crash in NSURLConnection
andymatuschak authored
70 [downloadFilename release];
71 downloadFilename = [path copy];
72 }
73
2e3ed79 @andymatuschak Fixing #502212: clang static analyzer identifies 2 leaks in Sparkle code
andymatuschak authored
74 - (void)downloadDidFinish:(NSURLDownload *)aDownload
75 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
76 NSError *error = nil;
c34e9b3 @andymatuschak Added an extra null check to SUAppcast downloadDidFinish because NSXM…
andymatuschak authored
77
78 NSXMLDocument *document;
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
79 BOOL failed = NO;
80 NSArray *xmlItems = nil;
81 NSMutableArray *appcastItems = [NSMutableArray array];
c34e9b3 @andymatuschak Added an extra null check to SUAppcast downloadDidFinish because NSXM…
andymatuschak authored
82
83 if (downloadFilename)
84 {
85 document = [[[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:downloadFilename] options:0 error:&error] autorelease];
86
4fad530 Change availability checks so that they build on 10.4
Stuart Morgan authored
87 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
c34e9b3 @andymatuschak Added an extra null check to SUAppcast downloadDidFinish because NSXM…
andymatuschak authored
88 [[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
54299d7 added #ifdef's for Tiger support
August Joki authored
89 #else
c34e9b3 @andymatuschak Added an extra null check to SUAppcast downloadDidFinish because NSXM…
andymatuschak authored
90 [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:nil];
54299d7 added #ifdef's for Tiger support
August Joki authored
91 #endif
c34e9b3 @andymatuschak Added an extra null check to SUAppcast downloadDidFinish because NSXM…
andymatuschak authored
92 [downloadFilename release];
93 downloadFilename = nil;
94 }
95 else
96 {
97 failed = YES;
98 }
9d260f9 @andymatuschak Bug #275309: Crash in NSURLConnection
andymatuschak authored
99
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
100 if (nil == document)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
101 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
102 failed = YES;
103 }
104 else
105 {
106 xmlItems = [document nodesForXPath:@"/rss/channel/item" error:&error];
107 if (nil == xmlItems)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
108 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
109 failed = YES;
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
110 }
111 }
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
112
113 if (failed == NO)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
114 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
115
116 NSEnumerator *nodeEnum = [xmlItems objectEnumerator];
117 NSXMLNode *node;
a148504 @andymatuschak Fixes 252986
andymatuschak authored
118 NSMutableDictionary *nodesDict = [NSMutableDictionary dictionary];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
119 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
120
121 while (failed == NO && (node = [nodeEnum nextObject]))
122 {
a148504 @andymatuschak Fixes 252986
andymatuschak authored
123 // First, we'll "index" all the first-level children of this appcast item so we can pick them out by language later.
124 if ([[node children] count])
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
125 {
a148504 @andymatuschak Fixes 252986
andymatuschak authored
126 node = [node childAtIndex:0];
127 while (nil != node)
128 {
129 NSString *name = [node name];
0343569 @andymatuschak Fixes 256106
andymatuschak authored
130 if (name)
a148504 @andymatuschak Fixes 252986
andymatuschak authored
131 {
0343569 @andymatuschak Fixes 256106
andymatuschak authored
132 NSMutableArray *nodes = [nodesDict objectForKey:name];
133 if (nodes == nil)
134 {
135 nodes = [NSMutableArray array];
136 [nodesDict setObject:nodes forKey:name];
137 }
138 [nodes addObject:node];
a148504 @andymatuschak Fixes 252986
andymatuschak authored
139 }
140 node = [node nextSibling];
141 }
142 }
143
144 NSEnumerator *nameEnum = [nodesDict keyEnumerator];
145 NSString *name;
146 while ((name = [nameEnum nextObject]))
147 {
148 node = [self bestNodeInNodes:[nodesDict objectForKey:name]];
149 if ([name isEqualToString:@"enclosure"])
150 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
151 // enclosure is flattened as a separate dictionary for some reason
6e648f1 Add support for parsing an extended <sparkle:deltas> element in the a…
Mark Rowe authored
152 NSDictionary *encDict = [(NSXMLElement *)node attributesAsDictionary];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
153 [dict setObject:encDict forKey:@"enclosure"];
154
155 }
156 else if ([name isEqualToString:@"pubDate"])
157 {
158 // pubDate is expected to be an NSDate by SUAppcastItem, but the RSS class was returning an NSString
159 NSDate *date = [NSDate dateWithNaturalLanguageString:[node stringValue]];
160 if (date)
161 [dict setObject:date forKey:name];
162 }
6e648f1 Add support for parsing an extended <sparkle:deltas> element in the a…
Mark Rowe authored
163 else if ([name isEqualToString:@"sparkle:deltas"])
164 {
165 NSMutableArray *deltas = [NSMutableArray array];
166 NSEnumerator *childEnum = [[node children] objectEnumerator];
167 NSXMLNode *child;
168 while ((child = [childEnum nextObject])) {
169 if ([[child name] isEqualToString:@"enclosure"])
170 [deltas addObject:[(NSXMLElement *)child attributesAsDictionary]];
171 }
172 [dict setObject:deltas forKey:@"deltas"];
173 }
174 else if (name != nil)
175 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
176 // add all other values as strings
177 [dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
178 }
a148504 @andymatuschak Fixes 252986
andymatuschak authored
179 }
180
7bfbe83 @nevyn When failing to parse a feed item, tell the developer *what* the erro…
nevyn authored
181 NSString *errString;
b21b308 all but a few places use the typical [[[foo alloc] init] autorelease]…
Sean McBride authored
182 SUAppcastItem *anItem = [[[SUAppcastItem alloc] initWithDictionary:dict failureReason:&errString] autorelease];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
183 if (anItem)
184 {
185 [appcastItems addObject:anItem];
186 }
187 else
188 {
7bfbe83 @nevyn When failing to parse a feed item, tell the developer *what* the erro…
nevyn authored
189 NSLog(@"Sparkle Updater: Failed to parse appcast item: %@.\nAppcast dictionary was: %@", errString, dict);
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
190 }
a148504 @andymatuschak Fixes 252986
andymatuschak authored
191 [nodesDict removeAllObjects];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
192 [dict removeAllObjects];
193 }
194 }
195
196 if ([appcastItems count])
197 {
198 NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO] autorelease];
199 [appcastItems sortUsingDescriptors:[NSArray arrayWithObject:sort]];
200 items = [appcastItems copy];
201 }
202
203 if (failed)
204 {
205 [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, nil]]];
206 }
207 else if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)])
208 {
209 [delegate appcastDidFinishLoading:self];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
210 }
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
211 }
212
a5245ac @andymatuschak Fixed a few local-vs-ivar variable ambiguities.
andymatuschak authored
213 - (void)download:(NSURLDownload *)aDownload didFailWithError:(NSError *)error
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
214 {
bd80144 @andymatuschak Fixing #456514: Appcast download crashes on 10.5 if download fails
andymatuschak authored
215 if (downloadFilename)
216 {
4fad530 Change availability checks so that they build on 10.4
Stuart Morgan authored
217 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
bd80144 @andymatuschak Fixing #456514: Appcast download crashes on 10.5 if download fails
andymatuschak authored
218 [[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
54299d7 added #ifdef's for Tiger support
August Joki authored
219 #else
d7774c0 performed a code review, specifically:
Sean McBride authored
220 [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:nil];
54299d7 added #ifdef's for Tiger support
August Joki authored
221 #endif
bd80144 @andymatuschak Fixing #456514: Appcast download crashes on 10.5 if download fails
andymatuschak authored
222 }
9d260f9 @andymatuschak Bug #275309: Crash in NSURLConnection
andymatuschak authored
223 [downloadFilename release];
224 downloadFilename = nil;
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
225
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
226 [self reportError:error];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
227 }
228
a5245ac @andymatuschak Fixed a few local-vs-ivar variable ambiguities.
andymatuschak authored
229 - (NSURLRequest *)download:(NSURLDownload *)aDownload willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
230 {
231 return request;
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
232 }
233
234 - (void)reportError:(NSError *)error
235 {
236 if ([delegate respondsToSelector:@selector(appcast:failedToLoadWithError:)])
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
237 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
238 [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]]];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
239 }
240 }
241
a148504 @andymatuschak Fixes 252986
andymatuschak authored
242 - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes
243 {
244 // We use this method to pick out the localized version of a node when one's available.
245 if ([nodes count] == 1)
246 return [nodes objectAtIndex:0];
247 else if ([nodes count] == 0)
248 return nil;
249
250 NSEnumerator *nodeEnum = [nodes objectEnumerator];
251 NSXMLElement *node;
252 NSMutableArray *languages = [NSMutableArray array];
253 NSString *lang;
d7774c0 performed a code review, specifically:
Sean McBride authored
254 NSUInteger i;
a148504 @andymatuschak Fixes 252986
andymatuschak authored
255 while ((node = [nodeEnum nextObject]))
256 {
257 lang = [[node attributeForName:@"xml:lang"] stringValue];
a032358 @andymatuschak Removing GNU-style ?: operators. Thanks for the report, Uli.
andymatuschak authored
258 [languages addObject:(lang ? lang : @"")];
a148504 @andymatuschak Fixes 252986
andymatuschak authored
259 }
260 lang = [[NSBundle preferredLocalizationsFromArray:languages] objectAtIndex:0];
0343569 @andymatuschak Fixes 256106
andymatuschak authored
261 i = [languages indexOfObject:([languages containsObject:lang] ? lang : @"")];
a148504 @andymatuschak Fixes 252986
andymatuschak authored
262 if (i == NSNotFound)
263 i = 0;
264 return [nodes objectAtIndex:i];
265 }
266
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
267 - (void)setUserAgentString:(NSString *)uas
268 {
a5dcf94 @andymatuschak Fixed all setters to eliminate the possibility of accidentally releas…
andymatuschak authored
269 if (uas != userAgentString)
270 {
271 [userAgentString release];
272 userAgentString = [uas copy];
273 }
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
274 }
275
276 - (void)setDelegate:del
277 {
278 delegate = del;
279 }
280
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
281 @end
Something went wrong with that request. Please try again.