Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
12 @interface SUAppcast (Private)
13 - (void)reportError:(NSError *)error;
a148504 Andy Matuschak Fixes 252986
andymatuschak authored
14 - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
d06b2f5 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
15 @end
9fa3da5 Holy restructuring, batman! Watch out for falling folders.
andym authored
16
d06b2f5 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
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 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
30 - (void)fetchAppcastFromURL:(NSURL *)url
31 {
82c2448 Andy Matuschak 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 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
39 }
40
82c2448 Andy Matuschak 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 Andy Matuschak 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 Andy Matuschak Fixed 243883:
andymatuschak authored
57 {
82c2448 Andy Matuschak 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 Andy Matuschak Fixed 243883:
andymatuschak authored
64 {
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
65 failed = YES;
74337fb Andy Matuschak Fixed 243883:
andymatuschak authored
66 }
67 }
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
68
69 if (failed == NO)
74337fb Andy Matuschak Fixed 243883:
andymatuschak authored
70 {
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
71
72 NSEnumerator *nodeEnum = [xmlItems objectEnumerator];
73 NSXMLNode *node;
a148504 Andy Matuschak Fixes 252986
andymatuschak authored
74 NSMutableDictionary *nodesDict = [NSMutableDictionary dictionary];
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
75 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
76
77 while (failed == NO && (node = [nodeEnum nextObject]))
78 {
a148504 Andy Matuschak 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 Andy Matuschak Fixes 244419
andymatuschak authored
81 {
a148504 Andy Matuschak 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 Andy Matuschak 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 Andy Matuschak If Sparkle can't parse one item, it no longer fails the entire appcast.
andymatuschak authored
122 else if (name != nil)
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
123 {
124 // add all other values as strings
125 [dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
126 }
a148504 Andy Matuschak Fixes 252986
andymatuschak authored
127 }
128
82c2448 Andy Matuschak 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 Andy Matuschak 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 Andy Matuschak Fixes 244419
andymatuschak authored
138 }
a148504 Andy Matuschak Fixes 252986
andymatuschak authored
139 [nodesDict removeAllObjects];
82c2448 Andy Matuschak 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 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
161 }
162
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
163 - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error
d06b2f5 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
164 {
82c2448 Andy Matuschak Fixes 244419
andymatuschak authored
165 CFRelease(connection);
166
d06b2f5 Andy Matuschak Fixes bug #228449: now the appcast doesn't run the RSS feed fetching in ...
andymatuschak authored
167 [self reportError:error];
82c2448 Andy Matuschak 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 refact...
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 refact...
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 Andy Matuschak 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 refact...
andym authored
208 - (void)setUserAgentString:(NSString *)uas
209 {
a5dcf94 Andy Matuschak Fixed all setters to eliminate the possibility of accidentally releasing...
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 refact...
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.