diff --git a/RSS.h b/RSS.h index 48bf15bd8..38978b2b3 100644 --- a/RSS.h +++ b/RSS.h @@ -50,6 +50,8 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMA @interface RSS : NSObject { + NSMutableData *incrementalData; + id delegate; NSDictionary *headerItems; NSMutableArray *newsItems; @@ -59,44 +61,17 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMA BOOL normalize; } +- (RSS *)initWithURL:(NSURL *) url userAgent:(NSString*)userAgent delegate:delegate; +- (BOOL)loadData:(NSData *)rssData normalize:(BOOL)fl; -/*Public*/ -- (RSS *)initWithURL:(NSURL *) url normalize:(BOOL) fl userAgent:(NSString*)userAgent error:(NSError **)error; - -- (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) description; - -- (RSS *) initWithData: (NSData *) rssData normalize: (BOOL) fl; - -- (NSDictionary *) headerItems; - -- (NSMutableArray *) newsItems; - -- (NSString *) version; - -// AMM's extensions for Sparkle +- (NSMutableArray *)newsItems; - (NSDictionary *)newestItem; +@end -/*Private*/ - -- (void) createheaderdictionary: (CFXMLTreeRef) tree; - -- (void) createitemsarray: (CFXMLTreeRef) tree; - -- (void) setversionstring: (CFXMLTreeRef) tree; - -- (void) flattenimagechildren: (CFXMLTreeRef) tree into: (NSMutableDictionary *) dictionary; - -- (void) flattensourceattributes: (CFXMLNodeRef) node into: (NSMutableDictionary *) dictionary; - -- (CFXMLTreeRef) getchanneltree: (CFXMLTreeRef) tree; - -- (CFXMLTreeRef) getnamedtree: (CFXMLTreeRef) currentTree name: (NSString *) name; - -- (void) normalizeRSSItem: (NSMutableDictionary *) rssItem; - -- (NSString *) getelementvalue: (CFXMLTreeRef) tree; - +@interface NSObject (RSSDelegateProtocol) +- (void)feedDidFinishLoading:(RSS *)feed; +- (void)feed:(RSS *)feed didFailWithError:(NSError *)error; @end #endif diff --git a/RSS.m b/RSS.m index ac76e8cb8..ea4efa0e4 100644 --- a/RSS.m +++ b/RSS.m @@ -49,6 +49,18 @@ - (NSString *)localizedFailureReason; @end #endif +@interface RSS (Private) +- (void) createheaderdictionary: (CFXMLTreeRef) tree; +- (void) createitemsarray: (CFXMLTreeRef) tree; +- (void) setversionstring: (CFXMLTreeRef) tree; +- (void) flattenimagechildren: (CFXMLTreeRef) tree into: (NSMutableDictionary *) dictionary; +- (void) flattensourceattributes: (CFXMLNodeRef) node into: (NSMutableDictionary *) dictionary; +- (CFXMLTreeRef) getchanneltree: (CFXMLTreeRef) tree; +- (CFXMLTreeRef) getnamedtree: (CFXMLTreeRef) currentTree name: (NSString *) name; +- (void) normalizeRSSItem: (NSMutableDictionary *) rssItem; +- (NSString *) getelementvalue: (CFXMLTreeRef) tree; +@end + // This comparator function is used to sort the RSS items by their published date. NSComparisonResult compareNewsItems(id item1, id item2, void *context) { @@ -98,72 +110,79 @@ - (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) descrip } /*initWithTitle*/ -- (RSS *) initWithData: (NSData *) rssData normalize: (BOOL) fl { - +- (BOOL)loadData:(NSData *)rssData normalize:(BOOL)fl +{ CFXMLTreeRef tree; - flRdf = NO; - normalize = fl; NS_DURING - tree = CFXMLTreeCreateFromData (kCFAllocatorDefault, (CFDataRef) rssData, NULL, kCFXMLParserSkipWhitespace, kCFXMLNodeCurrentVersion); - NS_HANDLER - tree = nil; - NS_ENDHANDLER - if (tree == nil) { - - /*If there was a problem parsing the RSS file, - raise an exception.*/ - - [self release]; - return nil; - } /*if*/ + if (tree == nil) + return NO; [self createheaderdictionary: tree]; - [self createitemsarray: tree]; - [self setversionstring: tree]; - CFRelease (tree); + return YES; +} + - return (self); - } /*initWithData*/ +- (RSS *)initWithURL:(NSURL *)url userAgent:(NSString*)userAgent delegate:del +{ + self = [super init]; + if (self) + { + delegate = del; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url cachePolicy: NSURLRequestReloadIgnoringCacheData + timeoutInterval: 30.0]; + if (userAgent) + [request setValue: userAgent forHTTPHeaderField: @"User-Agent"]; + + incrementalData = [[NSMutableData data] retain]; + NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self]; + CFRetain(connection); + } + return self; +} +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [incrementalData appendData:data]; +} - - -- (RSS *)initWithURL:(NSURL *)url normalize:(BOOL)fl userAgent:(NSString*)userAgent error:(NSError **)error +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url cachePolicy: NSURLRequestReloadIgnoringCacheData - timeoutInterval: 30.0]; - if (userAgent) - [request setValue: userAgent forHTTPHeaderField: @"User-Agent"]; - - NSURLResponse *response = nil; - NSData *rssData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error]; - if (rssData == nil) { return nil; } + CFRelease(connection); + NSError *error = nil; @try { - [self initWithData:rssData normalize:fl]; + if (![self loadData:incrementalData normalize:YES]) + error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObject:SULocalizedString(@"An error occurred while parsing the update feed.", nil) forKey:NSLocalizedDescriptionKey]]; } @catch (NSException *parseException) { - if (error) - *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], NSLocalizedFailureReasonErrorKey, nil]]; - return nil; + error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"An error occurred while parsing the update feed: %@", nil), [parseException reason]] forKey:NSLocalizedFailureReasonErrorKey]]; } - return self; + + if (error) + [delegate feed:self didFailWithError:error]; + else + [delegate feedDidFinishLoading:self]; } +- (void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error +{ + CFRelease(connection); + [delegate feed:self didFailWithError:error]; +} - (NSDictionary *) headerItems { @@ -184,7 +203,7 @@ - (NSString *) version { - (void) dealloc { - + [incrementalData release]; [headerItems release]; [newsItems release]; diff --git a/SUAppcast.m b/SUAppcast.m index 9a400931f..fdd14df4c 100644 --- a/SUAppcast.m +++ b/SUAppcast.m @@ -9,12 +9,11 @@ #import "Sparkle.h" #import "SUAppcast.h" -@implementation SUAppcast +@interface SUAppcast (Private) +- (void)reportError:(NSError *)error; +@end -- (void)fetchAppcastFromURL:(NSURL *)url -{ - [NSThread detachNewThreadSelector:@selector(_fetchAppcastFromURL:) toTarget:self withObject:url]; -} +@implementation SUAppcast - (void)dealloc { @@ -27,18 +26,14 @@ - (NSArray *)items return items; } -- (void)_fetchAppcastFromURL:(NSURL *)url +- (void)fetchAppcastFromURL:(NSURL *)url +{ + RSS *feed = [[RSS alloc] initWithURL:url userAgent:userAgentString delegate:self]; + CFRetain(feed); // Manage the RSS feed's memory manually. +} + +- (void)feedDidFinishLoading:(RSS *)feed { - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - NSError *error = nil; - RSS *feed = [[RSS alloc] initWithURL:url normalize:YES userAgent:userAgentString error:&error]; - if (!feed) - { - [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO]; - return; - } - // Set up all the appcast items: items = [NSMutableArray array]; id enumerator = [[feed newsItems] objectEnumerator], current; @@ -51,8 +46,7 @@ - (void)_fetchAppcastFromURL:(NSURL *)url } @catch (NSException *parseException) { - error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], SUTechnicalErrorInformationKey, nil]]; - [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO]; + [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], SUTechnicalErrorInformationKey, nil]]]; return; } items = [[NSArray arrayWithArray:items] retain]; // Make the items list immutable. @@ -60,8 +54,12 @@ - (void)_fetchAppcastFromURL:(NSURL *)url if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)]) [delegate performSelectorOnMainThread:@selector(appcastDidFinishLoading:) withObject:self waitUntilDone:NO]; - [feed release]; - [pool release]; + CFRelease(feed); +} + +- (void)feed:(RSS *)feed didFailWithError:(NSError *)error +{ + [self reportError:error]; } - (void)reportError:(NSError *)error