Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixes 244419

Now removing the RSS class and and NSString+extras to be replaced by a much simpler implementation based on NSXMLDocument directly in SUAppcast! But this will probably break things for certain edge-case appcasts, so please file bugs if things no longer work for you.

Thanks to Christiaan Hofman for the patch!
  • Loading branch information...
commit 82c2448321b9a490b6af958a7ceeb1108bd74770 1 parent a67b546
@andymatuschak andymatuschak authored
View
66 NSString+extras.h
@@ -1,66 +0,0 @@
-/*
-
-BSD License
-
-Copyright (c) 2002, Brent Simmons
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-* Neither the name of ranchero.com or Brent Simmons nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-*/
-
-#ifndef NSSTRING_PLUS_EXTRAS_H
-#define NSSTRING_PLUS_EXTRAS_H
-
-
-/*
- NSString+extras.h
- NetNewsWire
-
- Created by Brent Simmons on Fri Jun 14 2002.
- Copyright (c) 2002 Brent Simmons. All rights reserved.
-*/
-
-
-#import <Foundation/Foundation.h>
-#import <CoreFoundation/CoreFoundation.h>
-#import "SUConstants.h" // for NSInteger
-
-@interface NSString (BSextras)
-
-- (NSString *)stringWithSubstitute:(NSString *)subs forCharactersFromSet:(NSCharacterSet *)set;
-
-- (NSString *) trimWhiteSpace;
-
-- (NSString *) stripHTML;
-
-- (NSString *) ellipsizeAfterNWords: (NSInteger) n;
-
-+ (BOOL) stringIsEmpty: (NSString *) s;
-
-
-@end
-
-#endif
View
136 NSString+extras.m
@@ -1,136 +0,0 @@
-/*
-
-BSD License
-
-Copyright (c) 2002, Brent Simmons
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-* Neither the name of ranchero.com or Brent Simmons nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-*/
-
-
-/*
- NSString+extras.m
- NetNewsWire
-
- Created by Brent Simmons on Fri Jun 14 2002.
- Copyright (c) 2002 Brent Simmons. All rights reserved.
-*/
-
-#import "Sparkle.h"
-#import "NSString+extras.h"
-
-
-@implementation NSString (BSextras)
-
-- (NSString *)stringWithSubstitute:(NSString *)subs forCharactersFromSet:(NSCharacterSet *)set
-{
- NSRange r = [self rangeOfCharacterFromSet:set];
- if (r.location == NSNotFound) return self;
- NSMutableString *newString = [self mutableCopy];
- do
- {
- [newString replaceCharactersInRange:r withString:subs];
- r = [newString rangeOfCharacterFromSet:set];
- }
- while (r.location != NSNotFound);
- return [newString autorelease];
-}
-
-- (NSString *) trimWhiteSpace {
-
- NSMutableString *s = [[self mutableCopy] autorelease];
-
- CFStringTrimWhitespace ((CFMutableStringRef) s);
-
- return (NSString *) [[s copy] autorelease];
- } /*trimWhiteSpace*/
-
-
-- (NSString *) ellipsizeAfterNWords: (NSInteger) n {
-
- NSArray *stringComponents = [self componentsSeparatedByString: @" "];
- NSMutableArray *componentsCopy = [stringComponents mutableCopy];
- NSInteger ix = n;
- NSInteger len = [componentsCopy count];
-
- if (len < n)
- ix = len;
-
- [componentsCopy removeObjectsInRange: NSMakeRange (ix, len - ix)];
-
- return [componentsCopy componentsJoinedByString: @" "];
- } /*ellipsizeAfterNWords*/
-
-
-- (NSString *) stripHTML {
-
- NSUInteger len = [self length];
- NSMutableString *s = [NSMutableString stringWithCapacity: len];
- NSUInteger i = 0, level = 0;
-
- for (i = 0; i < len; i++) {
-
- NSString *ch = [self substringWithRange: NSMakeRange (i, 1)];
-
- if ([ch isEqualTo: @"<"])
- level++;
-
- else if ([ch isEqualTo: @">"]) {
-
- level--;
-
- if (level == 0)
- [s appendString: @" "];
- } /*else if*/
-
- else if (level == 0)
- [s appendString: ch];
- } /*for*/
-
- return (NSString *) [[s copy] autorelease];
- } /*stripHTML*/
-
-
-+ (BOOL) stringIsEmpty: (NSString *) s {
-
- NSString *copy;
-
- if (s == nil)
- return (YES);
-
- if ([s isEqualTo: @""])
- return (YES);
-
- copy = [[s copy] autorelease];
-
- if ([[copy trimWhiteSpace] isEqualTo: @""])
- return (YES);
-
- return (NO);
- } /*stringIsEmpty*/
-
-@end
View
77 RSS.h
@@ -1,77 +0,0 @@
-/*
-
-BSD License
-
-Copyright (c) 2002, Brent Simmons
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-* Neither the name of ranchero.com or Brent Simmons nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-*/
-
-#ifndef RSS_H
-#define RSS_H
-
-
-/*
- RSS.h
- A class for reading RSS feeds.
-
- Created by Brent Simmons on Wed Apr 17 2002.
- Copyright (c) 2002 Brent Simmons. All rights reserved.
-*/
-
-
-#import <Cocoa/Cocoa.h>
-#import <CoreFoundation/CoreFoundation.h>
-#import "NSString+extras.h"
-
-
-@interface RSS : NSObject {
- NSMutableData *incrementalData;
- id delegate;
-
- NSDictionary *headerItems;
- NSMutableArray *newsItems;
- NSString *version;
-
- BOOL flRdf;
- BOOL normalize;
- }
-
-- (RSS *)initWithURL:(NSURL *) url userAgent:(NSString*)userAgent delegate:delegate;
-- (BOOL)loadData:(NSData *)rssData normalize:(BOOL)fl;
-
-- (NSMutableArray *)newsItems;
-- (NSDictionary *)newestItem;
-
-@end
-
-@interface NSObject (RSSDelegateProtocol)
-- (void)feedDidFinishLoading:(RSS *)feed;
-- (void)feed:(RSS *)feed didFailWithError:(NSError *)error;
-@end
-
-#endif
View
727 RSS.m
@@ -1,727 +0,0 @@
-/*
-
-BSD License
-
-Copyright (c) 2002, Brent Simmons
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-* Neither the name of ranchero.com or Brent Simmons nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-*/
-
-/*
- RSS.m
- A class for reading RSS feeds.
-
- Created by Brent Simmons on Wed Apr 17 2002.
- Copyright (c) 2002 Brent Simmons. All rights reserved.
-*/
-
-
-#import "Sparkle.h"
-#import "RSS.h"
-
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
-@interface NSError (TigerPlusMethods)
-- (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)
-{
- // We compare item2 with item1 instead of the other way 'round because we want descending, not ascending. Bit of a hack.
- return [(NSDate *)[NSDate dateWithNaturalLanguageString:[item2 objectForKey:@"pubDate"]] compare:(NSDate *)[NSDate dateWithNaturalLanguageString:[item1 objectForKey:@"pubDate"]]];
-}
-
-@implementation RSS
-
-
-#define titleKey @"title"
-#define linkKey @"link"
-#define descriptionKey @"description"
-
-
-/*Public interface*/
-
-- (NSDictionary *)newestItem
-{
- // The news items are already sorted by published date, descending.
- return [[self newsItems] objectAtIndex:0];
-}
-
-- (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) description {
-
- /*
- Create an empty feed. Useful for synthetic feeds.
- */
-
- NSMutableDictionary *header;
-
- flRdf = NO;
-
- header = [NSMutableDictionary dictionaryWithCapacity: 2];
-
- [header setObject: title forKey: titleKey];
-
- [header setObject: description forKey: descriptionKey];
-
- headerItems = (NSDictionary *) [header copy];
-
- newsItems = [[NSMutableArray alloc] initWithCapacity: 0];
-
- version = [[NSString alloc] initWithString: @"synthetic"];
-
- return (self);
- } /*initWithTitle*/
-
-
-- (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)
- return NO;
-
- [self createheaderdictionary: tree];
- [self createitemsarray: tree];
- [self setversionstring: tree];
-
- return YES;
-}
-
-
-- (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];
-}
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
- CFRelease(connection);
-
- NSError *error = nil;
- @try
- {
- 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)
- {
- 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]];
- }
-
- 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];
-}
-
-- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
-{
- return request;
-}
-
-- (NSDictionary *) headerItems {
-
- return (headerItems);
- } /*headerItems*/
-
-
-- (NSMutableArray *) newsItems {
-
- return (newsItems);
- } /*newsItems*/
-
-
-- (NSString *) version {
-
- return (version);
- } /*version*/
-
-
-- (void) dealloc {
- [incrementalData release];
- [headerItems release];
-
- [newsItems release];
-
- [version release];
- [super dealloc];
- } /*dealloc*/
-
-
-
-/*Private methods. Don't call these: they may change.*/
-
-
-- (void) createheaderdictionary: (CFXMLTreeRef) tree {
-
- CFXMLTreeRef channelTree, childTree;
- CFXMLNodeRef childNode;
- CFIndex childCount, i;
- NSString *childName;
- NSMutableDictionary *headerItemsMutable;
-
- channelTree = [self getchanneltree: tree];
-
- if (channelTree == nil) {
-
- NSException *exception = [NSException exceptionWithName: @"RSSCreateHeaderDictionaryFailed"
- reason: @"Couldn't find the channel tree." userInfo: nil];
-
- [exception raise];
- } /*if*/
-
- childCount = CFTreeGetChildCount (channelTree);
-
- headerItemsMutable = [NSMutableDictionary dictionaryWithCapacity: childCount];
-
- for (i = 0; i < childCount; i++) {
-
- childTree = CFTreeGetChildAtIndex (channelTree, i);
-
- childNode = CFXMLTreeGetNode (childTree);
-
- childName = (NSString *) CFXMLNodeGetString (childNode);
-
- if ([childName hasPrefix: @"rss:"])
- childName = [childName substringFromIndex: 4];
-
- if ([childName isEqualToString: @"item"])
- break;
-
- if ([childName isEqualTo: @"image"])
- [self flattenimagechildren: childTree into: headerItemsMutable];
-
- [headerItemsMutable setObject: [self getelementvalue: childTree] forKey: childName];
- } /*for*/
-
- headerItems = [headerItemsMutable copy];
- } /*initheaderdictionary*/
-
-
-- (void) createitemsarray: (CFXMLTreeRef) tree {
-
- CFXMLTreeRef channelTree, childTree, itemTree;
- CFXMLNodeRef childNode, itemNode;
- NSString *childName;
- NSString *itemName, *itemValue;
- CFIndex childCount, itemChildCount, i, j;
- NSMutableDictionary *itemDictionaryMutable;
- NSMutableArray *itemsArrayMutable;
-
- if (flRdf)
- channelTree = [self getnamedtree: tree name: @"rdf:RDF"];
- else
- channelTree = [self getchanneltree: tree];
-
- if (channelTree == nil) {
-
- NSException *exception = [NSException exceptionWithName: @"RSSCreateItemsArrayFailed"
- reason: @"Couldn't find the news items." userInfo: nil];
-
- [exception raise];
- } /*if*/
-
- childCount = CFTreeGetChildCount (channelTree);
-
- itemsArrayMutable = [NSMutableArray arrayWithCapacity: childCount];
-
- for (i = 0; i < childCount; i++) {
-
- childTree = CFTreeGetChildAtIndex (channelTree, i);
-
- childNode = CFXMLTreeGetNode (childTree);
-
- childName = (NSString *) CFXMLNodeGetString (childNode);
-
- if ([childName hasPrefix: @"rss:"])
- childName = [childName substringFromIndex: 4];
-
- if (![childName isEqualToString: @"item"])
- continue;
-
- itemChildCount = CFTreeGetChildCount (childTree);
-
- itemDictionaryMutable = [NSMutableDictionary dictionaryWithCapacity: itemChildCount];
-
- for (j = 0; j < itemChildCount; j++) {
-
- itemTree = CFTreeGetChildAtIndex (childTree, j);
-
- itemNode = CFXMLTreeGetNode (itemTree);
-
- itemName = (NSString *) CFXMLNodeGetString (itemNode);
-
- if ([itemName hasPrefix: @"rss:"])
- itemName = [itemName substringFromIndex: 4];
-
- if ([itemName isEqualTo:@"enclosure"])
- {
- // Hack to add attributes to the dictionary in addition to children. (AMM)
- const CFXMLElementInfo *websiteInfo = CFXMLNodeGetInfoPtr(itemNode);
- NSMutableDictionary *enclosureDictionary = [NSMutableDictionary dictionary];
- id keyEnumerator = [(NSDictionary *)websiteInfo->attributes keyEnumerator], current;
- while ((current = [keyEnumerator nextObject]))
- {
- NSString *escapedAttributeValue = [(NSDictionary *)websiteInfo->attributes objectForKey:current];
- CFStringRef attributeValue = CFXMLCreateStringByUnescapingEntities(kCFAllocatorDefault, (CFStringRef)escapedAttributeValue, NULL);
- if (attributeValue)
- {
- // Do a stringWithString dance rather than just casting so that it's GC safe without using the 10.4+ CFMakeCollectable.
- [enclosureDictionary setObject:[NSString stringWithString:(NSString *)attributeValue] forKey:current];
- CFRelease(attributeValue);
- }
- }
- [itemDictionaryMutable setObject: enclosureDictionary forKey: itemName];
- continue;
- }
-
- itemValue = [self getelementvalue: itemTree];
-
- if ([itemName isEqualTo: @"source"])
- [self flattensourceattributes: itemNode into: itemDictionaryMutable];
-
- [itemDictionaryMutable setObject: itemValue forKey: itemName];
- } /*for*/
-
- if (normalize)
- [self normalizeRSSItem: itemDictionaryMutable];
-
- [itemsArrayMutable addObject: itemDictionaryMutable];
- } /*for*/
-
- // Sort the news items by published date, descending.
- newsItems = [[itemsArrayMutable sortedArrayUsingFunction:compareNewsItems context:NULL] retain];
- } /*createitemsarray*/
-
-
-- (void) setversionstring: (CFXMLTreeRef) tree {
-
- CFXMLTreeRef rssTree;
- const CFXMLElementInfo *elementInfo;
- CFXMLNodeRef node;
-
- if (flRdf) {
-
- version = [[NSString alloc] initWithString: @"rdf"];
-
- return;
- } /*if*/
-
- rssTree = [self getnamedtree: tree name: @"rss"];
-
- node = CFXMLTreeGetNode (rssTree);
-
- elementInfo = CFXMLNodeGetInfoPtr (node);
-
- version = [[NSString alloc] initWithString: [(NSDictionary *) (*elementInfo).attributes objectForKey: @"version"]];
- } /*setversionstring*/
-
-
-- (void) flattenimagechildren: (CFXMLTreeRef) tree into: (NSMutableDictionary *) dictionary {
-
- CFIndex childCount = CFTreeGetChildCount (tree);
- CFIndex i = 0;
- CFXMLTreeRef childTree;
- CFXMLNodeRef childNode;
- NSString *childName, *childValue, *keyName;
-
- if (childCount < 1)
- return;
-
- for (i = 0; i < childCount; i++) {
-
- childTree = CFTreeGetChildAtIndex (tree, i);
-
- childNode = CFXMLTreeGetNode (childTree);
-
- childName = (NSString *) CFXMLNodeGetString (childNode);
-
- if ([childName hasPrefix: @"rss:"])
- childName = [childName substringFromIndex: 4];
-
- childValue = [self getelementvalue: childTree];
-
- keyName = [NSString stringWithFormat: @"image%@", childName];
-
- [dictionary setObject: childValue forKey: keyName];
- } /*for*/
- } /*flattenimagechildren*/
-
-
-- (void) flattensourceattributes: (CFXMLNodeRef) node into: (NSMutableDictionary *) dictionary {
-
- const CFXMLElementInfo *elementInfo;
- NSString *sourceHomeUrl, *sourceRssUrl;
-
- elementInfo = CFXMLNodeGetInfoPtr (node);
-
- sourceHomeUrl = [(NSDictionary *) (*elementInfo).attributes objectForKey: @"homeUrl"];
-
- sourceRssUrl = [(NSDictionary *) (*elementInfo).attributes objectForKey: @"url"];
-
- if (sourceHomeUrl != nil)
- [dictionary setObject: sourceHomeUrl forKey: @"sourceHomeUrl"];
-
- if (sourceRssUrl != nil)
- [dictionary setObject: sourceRssUrl forKey: @"sourceRssUrl"];
- } /*flattensourceattributes*/
-
-
-- (CFXMLTreeRef) getchanneltree: (CFXMLTreeRef) tree {
-
- CFXMLTreeRef rssTree, channelTree;
-
- rssTree = [self getnamedtree: tree name: @"rss"];
-
- if (rssTree == nil) { /*It might be "rdf:RDF" instead, a 1.0 or greater feed.*/
-
- rssTree = [self getnamedtree: tree name: @"rdf:RDF"];
-
- if (rssTree != nil)
- flRdf = YES; /*This info will be needed later when creating the items array.*/
- } /*if*/
-
- if (rssTree == nil)
- return (nil);
-
- channelTree = [self getnamedtree: rssTree name: @"channel"];
-
- if (channelTree == nil)
- channelTree = [self getnamedtree: rssTree name: @"rss:channel"];
-
- return (channelTree);
- } /*getchanneltree*/
-
-
-- (CFXMLTreeRef) getnamedtree: (CFXMLTreeRef) currentTree name: (NSString *) name {
-
- CFIndex childCount, i;
- CFXMLNodeRef xmlNode;
- CFXMLTreeRef xmlTreeNode;
- NSString *itemName;
-
- childCount = CFTreeGetChildCount (currentTree);
-
- for (i = childCount - 1; i >= 0; i--) {
-
- xmlTreeNode = CFTreeGetChildAtIndex (currentTree, i);
-
- xmlNode = CFXMLTreeGetNode (xmlTreeNode);
-
- itemName = (NSString *) CFXMLNodeGetString (xmlNode);
-
- if ([itemName isEqualToString: name])
- return (xmlTreeNode);
- } /*for*/
-
- return (nil);
- } /*getnamedtree*/
-
-
-- (void) normalizeRSSItem: (NSMutableDictionary *) rssItem {
-
- /*
- Make sure item, link, and description are present and have
- reasonable values. Description and link may be "".
- Also trim white space, remove HTML when appropriate.
- */
-
- NSString *description, *normalizedLink, *title;
- BOOL nilDescription = NO;
-
- /*Description*/
-
- description = [rssItem objectForKey: descriptionKey];
-
- if (description == nil) {
-
- description = @"";
-
- nilDescription = YES;
- } /*if*/
-
- description = [description trimWhiteSpace];
-
- if ([description isEqualTo: @""])
- nilDescription = YES;
-
- [rssItem setObject: description forKey: descriptionKey];
-
- /*Link*/
-
- normalizedLink = [rssItem objectForKey: linkKey];
-
- if ([NSString stringIsEmpty: normalizedLink]) {
-
- /*Try to get a URL from the description.*/
-
- if (!nilDescription) {
-
- NSArray *stringComponents = [description componentsSeparatedByString: @"href=\""];
-
- if ([stringComponents count] > 1) {
-
- normalizedLink = [stringComponents objectAtIndex: 1];
-
- stringComponents = [normalizedLink componentsSeparatedByString: @"\""];
-
- normalizedLink = [stringComponents objectAtIndex: 0];
- } /*if*/
- } /*if*/
- } /*if*/
-
- if (normalizedLink == nil)
- normalizedLink = @"";
-
- normalizedLink = [normalizedLink trimWhiteSpace];
-
- [rssItem setObject: normalizedLink forKey: linkKey];
-
- /*Title*/
-
- title = [rssItem objectForKey: titleKey];
-
- if (title != nil) {
-
- title = [title stripHTML];
-
- title = [title trimWhiteSpace];
- } /*if*/
-
- if ([NSString stringIsEmpty: title]) {
-
- /*Grab a title from the description.*/
-
- if (!nilDescription) {
-
- NSArray *stringComponents = [description componentsSeparatedByString: @">"];
-
- if ([stringComponents count] > 1) {
-
- title = [stringComponents objectAtIndex: 1];
-
- stringComponents = [title componentsSeparatedByString: @"<"];
-
- title = [stringComponents objectAtIndex: 0];
-
- title = [title stripHTML];
-
- title = [title trimWhiteSpace];
- } /*if*/
-
- if ([NSString stringIsEmpty: title]) { /*use first part of description*/
-
- NSString *shortTitle = [[[description stripHTML] trimWhiteSpace] ellipsizeAfterNWords: 5];
-
- shortTitle = [shortTitle trimWhiteSpace];
-
- title = [NSString stringWithFormat: @"%@\u2026", shortTitle];
- } /*else*/
- } /*if*/
-
- title = [title stripHTML];
-
- title = [title trimWhiteSpace];
-
- if ([NSString stringIsEmpty: title])
- title = @"Untitled";
- } /*if*/
-
- [rssItem setObject: title forKey: titleKey];
-
- /*dangerousmeta case: super-long title with no description*/
-
- if ((nilDescription) && ([title length] > 50)) {
-
- NSString *shortTitle = [[[title stripHTML] trimWhiteSpace] ellipsizeAfterNWords: 7];
-
- description = [[title copy] autorelease];
-
- [rssItem setObject: description forKey: descriptionKey];
-
- title = [NSString stringWithFormat: @"%@\u2026", shortTitle];
-
- [rssItem setObject: title forKey: titleKey];
- } /*if*/
-
- { /*deal with entities*/
-
- const char *tempcstring;
- NSAttributedString *s = nil;
- NSString *convertedTitle = nil;
- NSArray *stringComponents;
-
- stringComponents = [title componentsSeparatedByString: @"&"];
-
- if ([stringComponents count] > 1) {
-
- stringComponents = [title componentsSeparatedByString: @";"];
-
- if ([stringComponents count] > 1) {
-
- size_t len;
-
- tempcstring = [title UTF8String];
-
- len = strlen (tempcstring);
-
- if (len > 0) {
-
- s = [[NSAttributedString alloc]
- initWithHTML: [NSData dataWithBytes: tempcstring length: strlen (tempcstring)]
- documentAttributes: (NSDictionary **) NULL];
-
- convertedTitle = [s string];
-
- [s autorelease];
-
- convertedTitle = [convertedTitle stripHTML];
-
- convertedTitle = [convertedTitle trimWhiteSpace];
- } /*if*/
-
- if ([NSString stringIsEmpty: convertedTitle])
- convertedTitle = @"Untitled";
-
- [rssItem setObject: convertedTitle forKey: @"convertedTitle"];
- } /*if*/
- } /*if*/
- } /*deal with entities*/
- } /*normalizeRSSItem*/
-
-
-- (NSString *) getelementvalue: (CFXMLTreeRef) tree {
-
- CFXMLNodeRef node;
- CFXMLTreeRef itemTree;
- CFIndex childCount, ix;
- NSMutableString *valueMutable;
- NSString *value;
- NSString *name;
-
- childCount = CFTreeGetChildCount (tree);
-
- valueMutable = [[NSMutableString alloc] init];
-
- for (ix = 0; ix < childCount; ix++) {
-
- itemTree = CFTreeGetChildAtIndex (tree, ix);
-
- node = CFXMLTreeGetNode (itemTree);
-
- name = (NSString *) CFXMLNodeGetString (node);
-
- if (name != nil) {
-
- if (CFXMLNodeGetTypeCode (node) == kCFXMLNodeTypeEntityReference) {
-
- if ([name isEqualTo: @"lt"])
- name = @"<";
-
- else if ([name isEqualTo: @"gt"])
- name = @">";
-
- else if ([name isEqualTo: @"quot"])
- name = @"\"";
-
- else if ([name isEqualTo: @"amp"])
- name = @"&";
-
- else if ([name isEqualTo: @"rsquo"]) {
- char rsquo[4] = { 0xe2, 0x80, 0x99, 0x0 };
- name = [NSString stringWithUTF8String: rsquo];
- }
-
- else if ([name isEqualTo: @"lsquo"]) {
- char lsquo[4] = { 0xe2, 0x80, 0x98, 0x0 };
- name = [NSString stringWithUTF8String: lsquo];
- }
-
- else if ([name isEqualTo: @"apos"])
- name = @"'";
- else
- name = [NSString stringWithFormat: @"&%@;", name];
- } /*if*/
-
- [valueMutable appendString: name];
- } /*if*/
- } /*for*/
-
- value = [[valueMutable trimWhiteSpace] retain];
-
- [valueMutable autorelease];
-
- return ([value autorelease]);
- } /*getelementvalue*/
-
-@end
View
2  SUAppcast.h
@@ -14,7 +14,7 @@
NSArray *items;
NSString *userAgentString;
id delegate;
- RSS *feed;
+ NSMutableData *incrementalData;
}
- (void)fetchAppcastFromURL:(NSURL *)url;
View
142 SUAppcast.m
@@ -17,7 +17,6 @@ @implementation SUAppcast
- (void)dealloc
{
- [feed release];
[items release];
[super dealloc];
}
@@ -29,48 +28,131 @@ - (NSArray *)items
- (void)fetchAppcastFromURL:(NSURL *)url
{
- feed = [[RSS alloc] initWithURL:url userAgent:userAgentString delegate:self];
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
+ if (userAgentString)
+ [request setValue:userAgentString forHTTPHeaderField:@"User-Agent"];
+
+ incrementalData = [[NSMutableData alloc] init];
+ NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
+ CFRetain(connection);
}
-- (void)feedDidFinishLoading:(RSS *)aFeed
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
- // Set up all the appcast items:
- NSArray *tempItems = [NSMutableArray array];
- id enumerator = [[feed newsItems] objectEnumerator], current;
- BOOL success = YES;
- while ((current = [enumerator nextObject]))
+ [incrementalData appendData:data];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
+{
+ CFRelease(connection);
+
+ NSError *error = nil;
+ NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:incrementalData options:0 error:&error];
+ BOOL failed = NO;
+ NSArray *xmlItems = nil;
+ NSMutableArray *appcastItems = [NSMutableArray array];
+
+ if (nil == document)
{
- SUAppcastItem *item = [[SUAppcastItem alloc] initWithDictionary:current];
- if (item)
- {
- [(NSMutableArray *)tempItems addObject:[item autorelease]];
- }
- else
+ failed = YES;
+ }
+ else
+ {
+ xmlItems = [document nodesForXPath:@"/rss/channel/item" error:&error];
+ if (nil == xmlItems)
{
- success = NO;
- break;
+ failed = YES;
}
}
- [feed release];
- feed = nil;
- if (success)
- {
- items = [tempItems copy]; // Make the items list immutable.
-
- if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)])
- [delegate performSelectorOnMainThread:@selector(appcastDidFinishLoading:) withObject:self waitUntilDone:NO];
- }
- else
+
+ if (failed == NO)
{
- [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, nil]]];
+
+ NSEnumerator *nodeEnum = [xmlItems objectEnumerator];
+ NSXMLNode *node;
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+
+ while (failed == NO && (node = [nodeEnum nextObject]))
+ {
+ // walk the children in reverse
+ node = [[node children] lastObject];
+ while (nil != node)
+ {
+
+ NSString *name = [node name];
+
+ if ([name isEqualToString:@"enclosure"]) {
+ // enclosure is flattened as a separate dictionary for some reason
+ NSEnumerator *attributeEnum = [[(NSXMLElement *)node attributes] objectEnumerator];
+ NSXMLNode *attribute;
+ NSMutableDictionary *encDict = [NSMutableDictionary dictionary];
+
+ while ((attribute = [attributeEnum nextObject]))
+ {
+ [encDict setObject:[attribute stringValue] forKey:[attribute name]];
+ }
+ [dict setObject:encDict forKey:@"enclosure"];
+
+ }
+ else if ([name isEqualToString:@"pubDate"])
+ {
+ // pubDate is expected to be an NSDate by SUAppcastItem, but the RSS class was returning an NSString
+ NSDate *date = [NSDate dateWithNaturalLanguageString:[node stringValue]];
+ if (date)
+ [dict setObject:date forKey:name];
+ }
+ else
+ {
+ // add all other values as strings
+ [dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
+ }
+
+ // previous sibling; returns nil when exhausted
+ node = [node previousSibling];
+ }
+ SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict];
+ if (anItem)
+ {
+ [appcastItems addObject:anItem];
+ [anItem release];
+ }
+ else
+ {
+ failed = YES;
+ }
+ [dict removeAllObjects];
+ }
+ }
+
+ [document release];
+
+ if ([appcastItems count])
+ {
+ NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO] autorelease];
+ [appcastItems sortUsingDescriptors:[NSArray arrayWithObject:sort]];
+ items = [appcastItems copy];
+ }
+
+ if (failed)
+ {
+ [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, nil]]];
+ }
+ else if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)])
+ {
+ [delegate appcastDidFinishLoading:self];
}
}
-- (void)feed:(RSS *)aFeed didFailWithError:(NSError *)error
+- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error
{
+ CFRelease(connection);
+
[self reportError:error];
- [feed release];
- feed = nil;
+}
+
+- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
+{
+ return request;
}
- (void)reportError:(NSError *)error
View
3  Sparkle.h
@@ -29,14 +29,11 @@
#import "NSFileManager+Aliases.h"
#import "NSFileManager+Authentication.h"
#import "NSFileManager+Verification.h"
-#import "NSString+extras.h"
#import "NSWorkspace_RBAdditions.h"
#import "NSWorkspace+SystemVersion.h"
#import "NTSynchronousTask.h"
-#import "RSS.h"
-
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUAutomaticUpdateAlert.h"
View
16 Sparkle.xcodeproj/project.pbxproj
@@ -69,10 +69,6 @@
61AAE8280A321A7F00D8810D /* Sparkle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8220A321A7F00D8810D /* Sparkle.strings */; };
61AAE8290A321A8000D8810D /* SUAutomaticUpdateAlert.nib in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8240A321A7F00D8810D /* SUAutomaticUpdateAlert.nib */; };
61AAE82A0A321A8000D8810D /* SUUpdateAlert.nib in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8260A321A7F00D8810D /* SUUpdateAlert.nib */; };
- 61B5F8E909C4CE3C00B25A18 /* NSString+extras.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5F8DF09C4CE3C00B25A18 /* NSString+extras.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61B5F8EA09C4CE3C00B25A18 /* NSString+extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F8E009C4CE3C00B25A18 /* NSString+extras.m */; };
- 61B5F8EB09C4CE3C00B25A18 /* RSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5F8E109C4CE3C00B25A18 /* RSS.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 61B5F8EC09C4CE3C00B25A18 /* RSS.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F8E209C4CE3C00B25A18 /* RSS.m */; };
61B5F8ED09C4CE3C00B25A18 /* SUUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */; settings = {ATTRIBUTES = (Public, ); }; };
61B5F8EE09C4CE3C00B25A18 /* SUUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F8E409C4CE3C00B25A18 /* SUUpdater.m */; };
61B5F8EF09C4CE3C00B25A18 /* NSFileManager+Authentication.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F8E509C4CE3C00B25A18 /* NSFileManager+Authentication.m */; };
@@ -225,10 +221,6 @@
61AAE84F0A321AF700D8810D /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Sparkle.strings; sourceTree = "<group>"; };
61AAE8590A321B0400D8810D /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Sparkle.strings; sourceTree = "<group>"; };
61AAE8710A321F7700D8810D /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Sparkle.strings; sourceTree = "<group>"; };
- 61B5F8DF09C4CE3C00B25A18 /* NSString+extras.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "NSString+extras.h"; sourceTree = "<group>"; };
- 61B5F8E009C4CE3C00B25A18 /* NSString+extras.m */ = {isa = PBXFileReference; fileEncoding = 30; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; path = "NSString+extras.m"; sourceTree = "<group>"; };
- 61B5F8E109C4CE3C00B25A18 /* RSS.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RSS.h; sourceTree = "<group>"; };
- 61B5F8E209C4CE3C00B25A18 /* RSS.m */ = {isa = PBXFileReference; fileEncoding = 30; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; path = RSS.m; sourceTree = "<group>"; };
61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SUUpdater.h; sourceTree = "<group>"; };
61B5F8E409C4CE3C00B25A18 /* SUUpdater.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SUUpdater.m; sourceTree = "<group>"; };
61B5F8E509C4CE3C00B25A18 /* NSFileManager+Authentication.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+Authentication.m"; sourceTree = "<group>"; };
@@ -439,10 +431,6 @@
61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */,
61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */,
61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */,
- 61B5F8E109C4CE3C00B25A18 /* RSS.h */,
- 61B5F8E209C4CE3C00B25A18 /* RSS.m */,
- 61B5F8DF09C4CE3C00B25A18 /* NSString+extras.h */,
- 61B5F8E009C4CE3C00B25A18 /* NSString+extras.m */,
);
name = "Appcast Support";
sourceTree = "<group>";
@@ -561,8 +549,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 61B5F8E909C4CE3C00B25A18 /* NSString+extras.h in Headers */,
- 61B5F8EB09C4CE3C00B25A18 /* RSS.h in Headers */,
61B5F8ED09C4CE3C00B25A18 /* SUUpdater.h in Headers */,
61B5FC0D09C4FC8200B25A18 /* SUAppcast.h in Headers */,
61B5FC7009C51F4A00B25A18 /* SUAppcastItem.h in Headers */,
@@ -825,8 +811,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 61B5F8EA09C4CE3C00B25A18 /* NSString+extras.m in Sources */,
- 61B5F8EC09C4CE3C00B25A18 /* RSS.m in Sources */,
61B5F8EE09C4CE3C00B25A18 /* SUUpdater.m in Sources */,
61B5F8EF09C4CE3C00B25A18 /* NSFileManager+Authentication.m in Sources */,
61B5FBB709C4FAFF00B25A18 /* SUAppcast.m in Sources */,
Please sign in to comment.
Something went wrong with that request. Please try again.