Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 223 lines (191 sloc) 7.001 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
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
12 @interface SUAppcast (Private)
13 - (void)reportError:(NSError *)error;
a148504 @andymatuschak Fixes 252986
andymatuschak authored
14 - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
15 @end
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
16
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
17 @implementation SUAppcast
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
18
19 - (void)dealloc
20 {
21 [items release];
22 [super dealloc];
23 }
24
25 - (NSArray *)items
26 {
27 return items;
28 }
29
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
30 - (void)fetchAppcastFromURL:(NSURL *)url
31 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
32 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
33 if (userAgentString)
34 [request setValue:userAgentString forHTTPHeaderField:@"User-Agent"];
35
36 incrementalData = [[NSMutableData alloc] init];
37 NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
38 CFRetain(connection);
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
39 }
40
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
41 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
42 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
43 [incrementalData appendData:data];
44 }
45
46 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
47 {
48 CFRelease(connection);
49
50 NSError *error = nil;
51 NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:incrementalData options:0 error:&error];
52 BOOL failed = NO;
53 NSArray *xmlItems = nil;
54 NSMutableArray *appcastItems = [NSMutableArray array];
55
56 if (nil == document)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
57 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
58 failed = YES;
59 }
60 else
61 {
62 xmlItems = [document nodesForXPath:@"/rss/channel/item" error:&error];
63 if (nil == xmlItems)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
64 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
65 failed = YES;
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
66 }
67 }
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
68
69 if (failed == NO)
74337fb @andymatuschak Fixed 243883:
andymatuschak authored
70 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
71
72 NSEnumerator *nodeEnum = [xmlItems objectEnumerator];
73 NSXMLNode *node;
a148504 @andymatuschak Fixes 252986
andymatuschak authored
74 NSMutableDictionary *nodesDict = [NSMutableDictionary dictionary];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
75 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
76
77 while (failed == NO && (node = [nodeEnum nextObject]))
78 {
a148504 @andymatuschak Fixes 252986
andymatuschak authored
79 // First, we'll "index" all the first-level children of this appcast item so we can pick them out by language later.
80 if ([[node children] count])
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
81 {
a148504 @andymatuschak Fixes 252986
andymatuschak authored
82 node = [node childAtIndex:0];
83 while (nil != node)
84 {
85 NSString *name = [node name];
86 NSMutableArray *nodes = [nodesDict objectForKey:name];
87 if (nodes == nil)
88 {
89 nodes = [NSMutableArray array];
90 [nodesDict setObject:nodes forKey:name];
91 }
92 [nodes addObject:node];
93
94 node = [node nextSibling];
95 }
96 }
97
98 NSEnumerator *nameEnum = [nodesDict keyEnumerator];
99 NSString *name;
100 while ((name = [nameEnum nextObject]))
101 {
102 node = [self bestNodeInNodes:[nodesDict objectForKey:name]];
103 if ([name isEqualToString:@"enclosure"])
104 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
105 // enclosure is flattened as a separate dictionary for some reason
106 NSEnumerator *attributeEnum = [[(NSXMLElement *)node attributes] objectEnumerator];
107 NSXMLNode *attribute;
108 NSMutableDictionary *encDict = [NSMutableDictionary dictionary];
109
110 while ((attribute = [attributeEnum nextObject]))
111 [encDict setObject:[attribute stringValue] forKey:[attribute name]];
112 [dict setObject:encDict forKey:@"enclosure"];
113
114 }
115 else if ([name isEqualToString:@"pubDate"])
116 {
117 // pubDate is expected to be an NSDate by SUAppcastItem, but the RSS class was returning an NSString
118 NSDate *date = [NSDate dateWithNaturalLanguageString:[node stringValue]];
119 if (date)
120 [dict setObject:date forKey:name];
121 }
bc7aff3 @andymatuschak If Sparkle can't parse one item, it no longer fails the entire appcast.
andymatuschak authored
122 else if (name != nil)
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
123 {
124 // add all other values as strings
125 [dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
126 }
a148504 @andymatuschak Fixes 252986
andymatuschak authored
127 }
128
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
129 SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict];
130 if (anItem)
131 {
132 [appcastItems addObject:anItem];
133 [anItem release];
134 }
135 else
136 {
bc7aff3 @andymatuschak If Sparkle can't parse one item, it no longer fails the entire appcast.
andymatuschak authored
137 NSLog(@"Sparkle Updater: Failed to parse appcast item with appcast dictionary %@!", dict);
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
138 }
a148504 @andymatuschak Fixes 252986
andymatuschak authored
139 [nodesDict removeAllObjects];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
140 [dict removeAllObjects];
141 }
142 }
143
144 [document release];
145
146 if ([appcastItems count])
147 {
148 NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO] autorelease];
149 [appcastItems sortUsingDescriptors:[NSArray arrayWithObject:sort]];
150 items = [appcastItems copy];
151 }
152
153 if (failed)
154 {
155 [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, nil]]];
156 }
157 else if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)])
158 {
159 [delegate appcastDidFinishLoading:self];
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
160 }
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
161 }
162
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
163 - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
164 {
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
165 CFRelease(connection);
166
d06b2f5 @andymatuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
andymatuschak authored
167 [self reportError:error];
82c2448 @andymatuschak Fixes 244419
andymatuschak authored
168 }
169
170 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
171 {
172 return request;
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
173 }
174
175 - (void)reportError:(NSError *)error
176 {
177 if ([delegate respondsToSelector:@selector(appcast:failedToLoadWithError:)])
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
178 {
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
179 [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
180 }
181 }
182
a148504 @andymatuschak Fixes 252986
andymatuschak authored
183 - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes
184 {
185 // We use this method to pick out the localized version of a node when one's available.
186 if ([nodes count] == 1)
187 return [nodes objectAtIndex:0];
188 else if ([nodes count] == 0)
189 return nil;
190
191 NSEnumerator *nodeEnum = [nodes objectEnumerator];
192 NSXMLElement *node;
193 NSMutableArray *languages = [NSMutableArray array];
194 NSString *lang;
195 NSInteger i;
196 while ((node = [nodeEnum nextObject]))
197 {
198 lang = [[node attributeForName:@"xml:lang"] stringValue];
199 [languages addObject:(lang ?: @"en")]; // Default to a key being English if no xml:lang is specified.
200 }
201 lang = [[NSBundle preferredLocalizationsFromArray:languages] objectAtIndex:0];
202 i = [languages indexOfObject:(lang ?: @"en")];
203 if (i == NSNotFound)
204 i = 0;
205 return [nodes objectAtIndex:i];
206 }
207
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
208 - (void)setUserAgentString:(NSString *)uas
209 {
a5dcf94 @andymatuschak Fixed all setters to eliminate the possibility of accidentally releas…
andymatuschak authored
210 if (uas != userAgentString)
211 {
212 [userAgentString release];
213 userAgentString = [uas copy];
214 }
bc3be9a Touched practically every line of code in a super-monster-awesome ref…
andym authored
215 }
216
217 - (void)setDelegate:del
218 {
219 delegate = del;
220 }
221
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
222 @end
Something went wrong with that request. Please try again.