Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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 appc...
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 in ...
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 in ...
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 in ...
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 in ...
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 in ...
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 NSXMLDo...
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 NSXMLDo...
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 NSXMLDo...
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 NSXMLDo...
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 NSXMLDo...
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 appc...
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 appc...
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 error i...
nevyn authored
181 NSString *errString;
b21b308 all but a few places use the typical [[[foo alloc] init] autorelease] pa...
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 error i...
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 in ...
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 in ...
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 in ...
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 refact...
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 refact...
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 refact...
andym authored
267 - (void)setUserAgentString:(NSString *)uas
268 {
a5dcf94 @andymatuschak Fixed all setters to eliminate the possibility of accidentally releasing...
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 refact...
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.