Permalink
Browse files

Add support for parsing an extended <sparkle:deltas> element in the a…

…ppcast that describes

the delta updates that are available for a given version.

The appcast would look a little something like the following:

<enclosure url="http://you.com/app/Your Great App 2.0.zip" sparkle:version="2.0" length="1623481" type="application/octet-stream" sparkle:dsaSignature="BAFJW4B6B1K1JyW30nbkBwainOzrN6EQuAh" />
<sparkle:deltas>
    <enclosure url="http://you.com/app/Your Great App 1.5 to 2.0.delta" sparkle:version="2.0" sparkle:deltaFrom="1.5" length="642381" type="application/octet-stream" sparkle:dsaSignature="MCa1JyW30nbkBwaC0CFBfeinOzrN6EQuAh=" />
    <enclosure url="http://you.com/app/Your Great App 1.4 to 2.0.delta" sparkle:version="2.0" sparkle:deltaFrom="1.4" length="928231" type="application/octet-stream" sparkle:dsaSignature="B6B1K1JyW30nbkBDBfeinOAszrN6Ea1JyW3" />
</sparkle:deltas>
  • Loading branch information...
1 parent c3d8254 commit 6e648f14f47f4823f65554e17938b3b1ea519b6f Mark Rowe committed Aug 21, 2009
Showing with 96 additions and 13 deletions.
  1. +32 −9 SUAppcast.m
  2. +5 −1 SUAppcastItem.h
  3. +31 −0 SUAppcastItem.m
  4. +2 −0 SUBasicUpdateDriver.h
  5. +26 −3 SUBasicUpdateDriver.m
View
@@ -9,6 +9,23 @@
#import "Sparkle.h"
#import "SUAppcast.h"
+@interface NSXMLElement (SUAppcastExtensions)
+- (NSDictionary *)attributesAsDictionary;
+@end
+
+@implementation NSXMLElement (SUAppcastExtensions)
+- (NSDictionary *)attributesAsDictionary
+{
+ NSEnumerator *attributeEnum = [[self attributes] objectEnumerator];
+ NSXMLNode *attribute;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ while ((attribute = [attributeEnum nextObject]))
+ [dictionary setObject:[attribute stringValue] forKey:[attribute name]];
+ return dictionary;
+}
+@end
+
@interface SUAppcast (Private)
- (void)reportError:(NSError *)error;
- (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
@@ -116,12 +133,7 @@ - (void)downloadDidFinish:(NSURLDownload *)download
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]];
+ NSDictionary *encDict = [(NSXMLElement *)node attributesAsDictionary];
[dict setObject:encDict forKey:@"enclosure"];
}
@@ -132,13 +144,24 @@ - (void)downloadDidFinish:(NSURLDownload *)download
if (date)
[dict setObject:date forKey:name];
}
- else if (name != nil)
- {
+ else if ([name isEqualToString:@"sparkle:deltas"])
+ {
+ NSMutableArray *deltas = [NSMutableArray array];
+ NSEnumerator *childEnum = [[node children] objectEnumerator];
+ NSXMLNode *child;
+ while ((child = [childEnum nextObject])) {
+ if ([[child name] isEqualToString:@"enclosure"])
+ [deltas addObject:[(NSXMLElement *)child attributesAsDictionary]];
+ }
+ [dict setObject:deltas forKey:@"deltas"];
+ }
+ else if (name != nil)
+ {
// add all other values as strings
[dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
}
}
-
+
SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict];
if (anItem)
{
View
@@ -22,7 +22,9 @@
NSURL *fileURL;
NSString *versionString;
NSString *displayVersionString;
-
+
+ NSDictionary *deltaUpdates;
+
NSDictionary *propertiesDictionary;
}
@@ -38,6 +40,8 @@
- (NSURL *)fileURL;
- (NSString *)DSASignature;
- (NSString *)minimumSystemVersion;
+- (NSDictionary *)deltaUpdates;
+- (BOOL)isDeltaUpdate;
// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions.
- (NSDictionary *)propertiesDictionary;
View
@@ -101,6 +101,19 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString
minimumSystemVersion = [systemVersionString copy];
}
+- (NSDictionary *)deltaUpdates { return [[deltaUpdates retain] autorelease]; }
+- (void)setDeltaUpdates:(NSDictionary *)updates
+{
+ if (deltaUpdates == updates) return;
+ [deltaUpdates release];
+ deltaUpdates = [updates copy];
+}
+
+- (BOOL)isDeltaUpdate
+{
+ return [[propertiesDictionary objectForKey:@"enclosure"] objectForKey:@"sparkle:deltaFrom"] != nil;
+}
+
- initWithDictionary:(NSDictionary *)dict
{
self = [super init];
@@ -157,6 +170,24 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString
[self setReleaseNotesURL:[NSURL URLWithString:[self itemDescription]]];
else
[self setReleaseNotesURL:nil];
+
+ if ([dict objectForKey:@"deltas"]) {
+ NSMutableDictionary *deltas = [NSMutableDictionary dictionary];
+ NSArray *deltaDictionaries = [dict objectForKey:@"deltas"];
+ NSEnumerator *deltaDictionariesEnum = [deltaDictionaries objectEnumerator];
+ NSDictionary *deltaDictionary;
+ while ((deltaDictionary = [deltaDictionariesEnum nextObject])) {
+ NSMutableDictionary *fakeAppCastDict = [dict mutableCopy];
+ [fakeAppCastDict removeObjectForKey:@"deltas"];
+ [fakeAppCastDict setObject:deltaDictionary forKey:@"enclosure"];
+ SUAppcastItem *deltaItem = [[[self class] alloc] initWithDictionary:fakeAppCastDict];
+ [fakeAppCastDict release];
+
+ [deltas setObject:deltaItem forKey:[deltaDictionary objectForKey:@"sparkle:deltaFrom"]];
+ [deltaItem release];
+ }
+ [self setDeltaUpdates:deltas];
+ }
}
}
return self;
@@ -15,6 +15,7 @@
@class SUAppcastItem, SUUnarchiver, SUAppcast, SUUnarchiver, SUHost;
@interface SUBasicUpdateDriver : SUUpdateDriver {
SUAppcastItem *updateItem;
+ SUAppcastItem *nonDeltaUpdateItem;
NSURLDownload *download;
NSString *downloadPath;
@@ -42,6 +43,7 @@
- (void)extractUpdate;
- (void)unarchiverDidFinish:(SUUnarchiver *)ua;
- (void)unarchiverDidFail:(SUUnarchiver *)ua;
+- (void)failedToApplyDeltaUpdate;
- (void)installUpdate;
- (void)installerFinishedForHost:(SUHost *)host;
View
@@ -94,9 +94,15 @@ - (void)appcastDidFinishLoading:(SUAppcast *)ac
do {
item = [updateEnumerator nextObject];
} while (item && ![self hostSupportsItem:item]);
+
+ SUAppcastItem *deltaUpdateItem = [[item deltaUpdates] objectForKey:[host version]];
+ if (deltaUpdateItem && [self hostSupportsItem:deltaUpdateItem]) {
+ nonDeltaUpdateItem = [item retain];
+ item = deltaUpdateItem;
+ }
}
-
- updateItem = [item retain];
+
+ updateItem = [item retain];
CFRelease(ac); // Remember that we're explicitly managing the memory of the appcast.
if (updateItem == nil) { [self didNotFindUpdate]; return; }
@@ -189,7 +195,7 @@ - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSS
- (void)extractUpdate
{
- SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath];
+ SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath updatingHost:host];
if (!unarchiver)
{
NSLog(@"Sparkle Error: No valid unarchiver for %@!", downloadPath);
@@ -201,6 +207,16 @@ - (void)extractUpdate
[unarchiver start];
}
+- (void)failedToApplyDeltaUpdate
+{
+ // When a delta update fails to apply we fall back on updating via a full install.
+ [updateItem release];
+ updateItem = nonDeltaUpdateItem;
+ nonDeltaUpdateItem = nil;
+
+ [self downloadUpdate];
+}
+
- (void)unarchiverDidFinish:(SUUnarchiver *)ua
{
if (ua) { CFRelease(ua); }
@@ -210,6 +226,12 @@ - (void)unarchiverDidFinish:(SUUnarchiver *)ua
- (void)unarchiverDidFail:(SUUnarchiver *)ua
{
if (ua) { CFRelease(ua); }
+
+ if ([updateItem isDeltaUpdate]) {
+ [self failedToApplyDeltaUpdate];
+ return;
+ }
+
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:[NSDictionary dictionaryWithObject:SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil) forKey:NSLocalizedDescriptionKey]]];
}
@@ -304,6 +326,7 @@ - (void)abortUpdateWithError:(NSError *)error
- (void)dealloc
{
[updateItem release];
+ [nonDeltaUpdateItem release];
[download release];
[downloadPath release];
[relaunchPath release];

0 comments on commit 6e648f1

Please sign in to comment.