Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge andymatuschak/Sparkle.

  • Loading branch information...
commit 9937e5a17b7149655268e8d7ebbede265b27a033 2 parents 67b0860 + 3ea1a9e
@uliwitness uliwitness authored
Showing with 3,645 additions and 809 deletions.
  1. +9 −0 Configurations/ConfigBinaryDelta.xcconfig
  2. +5 −0 Configurations/ConfigBinaryDeltaDebug.xcconfig
  3. +3 −0  Configurations/ConfigBinaryDeltaRelease.xcconfig
  4. +14 −4 Configurations/ConfigCommon.xcconfig
  5. +5 −2 Configurations/ConfigCommonDebug.xcconfig
  6. +9 −0 Configurations/ConfigUnitTest.xcconfig
  7. +3 −0  Configurations/ConfigUnitTestDebug.xcconfig
  8. +3 −0  Configurations/ConfigUnitTestRelease.xcconfig
  9. +3 −0  Configurations/ConfigUnitTestReleaseGCSupport.xcconfig
  10. +28 −1 License.txt
  11. +1 −0  NTSynchronousTask.h
  12. +112 −151 NTSynchronousTask.m
  13. +4 −1 SUAppcast.h
  14. +63 −37 SUAppcast.m
  15. +8 −2 SUAppcastItem.h
  16. +35 −0 SUAppcastItem.m
  17. +3 −1 SUAutomaticUpdateDriver.h
  18. +2 −0  SUBasicUpdateDriver.h
  19. +37 −9 SUBasicUpdateDriver.m
  20. +15 −0 SUBinaryDeltaApply.h
  21. +101 −0 SUBinaryDeltaApply.m
  22. +26 −0 SUBinaryDeltaCommon.h
  23. +165 −0 SUBinaryDeltaCommon.m
  24. +269 −0 SUBinaryDeltaTool.m
  25. +20 −0 SUBinaryDeltaUnarchiver.h
  26. +48 −0 SUBinaryDeltaUnarchiver.m
  27. +20 −20 SUConstants.h
  28. +23 −23 SUConstants.m
  29. +37 −20 SUDSAVerifier.m
  30. +21 −19 SUDiskImageUnarchiver.m
  31. +1 −0  SUHost.h
  32. +16 −10 SUHost.m
  33. +1 −4 SUInstaller.h
  34. +7 −9 SUInstaller.m
  35. +8 −12 SUPackageInstaller.m
  36. +25 −25 SUPipedUnarchiver.m
  37. +14 −15 SUPlainInstaller.m
  38. +13 −10 SUPlainInstallerInternals.m
  39. +3 −1 SUScheduledUpdateDriver.h
  40. +7 −5 SUStandardVersionComparator.m
  41. +3 −1 SUStatusController.h
  42. +3 −3 SUStatusController.m
  43. +16 −17 SUSystemProfiler.m
  44. +25 −22 SUUIBasedUpdateDriver.m
  45. +5 −2 SUUnarchiver.h
  46. +4 −6 SUUnarchiver.m
  47. +7 −7 SUUnarchiver_Private.h
  48. +17 −13 SUUnarchiver_Private.m
  49. +1 −1  SUUpdateAlert.m
  50. +1 −1  SUUpdateDriver.h
  51. +1 −1  SUUpdateDriver.m
  52. +1 −2  SUUpdatePermissionPrompt.m
  53. +1 −0  SUUpdater.h
  54. +15 −11 SUUpdater.m
  55. +3 −1 SUUserInitiatedUpdateDriver.h
  56. +17 −17 SUUserInitiatedUpdateDriver.m
  57. +2 −1  SUWindowController.m
  58. +15 −1 Sparkle.pch
  59. +299 −300 Sparkle.xcodeproj/project.pbxproj
  60. +501 −0 bsdiff.c
  61. +271 −0 bspatch.c
  62. +12 −0 bspatch.h
  63. BIN  cs.lproj/Sparkle.strings
  64. BIN  en.lproj/Sparkle.strings
  65. BIN  es.lproj/Sparkle.strings
  66. +3 −2 generate_keys.rb
  67. +344 −0 is.lproj/SUUpdateAlert.xib
  68. BIN  ja.lproj/Sparkle.strings
  69. BIN  pt_PT.lproj/Sparkle.strings
  70. +21 −11 relaunch.m
  71. BIN  sk.lproj/Sparkle.strings
  72. BIN  tr.lproj/Sparkle.strings
  73. +870 −8 zh_TW.lproj/SUUpdateAlert.xib
View
9 Configurations/ConfigBinaryDelta.xcconfig
@@ -0,0 +1,9 @@
+// BinaryDelta tool only
+
+PRODUCT_NAME = BinaryDelta
+GCC_PREFIX_HEADER =
+SDKROOT = macosx10.5
+MACOSX_DEPLOYMENT_TARGET[arch=i386] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=ppc64] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=ppc] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=x86_64] = 10.5
View
5 Configurations/ConfigBinaryDeltaDebug.xcconfig
@@ -0,0 +1,5 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonDebug.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
+
+OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
View
3  Configurations/ConfigBinaryDeltaRelease.xcconfig
@@ -0,0 +1,3 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonRelease.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
View
18 Configurations/ConfigCommon.xcconfig
@@ -3,7 +3,7 @@
PREBINDING = NO
ZERO_LINK = NO
CURRENT_PROJECT_VERSION = 1.5
-SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk
+SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk
MACOSX_DEPLOYMENT_TARGET[arch=ppc] = 10.4
MACOSX_DEPLOYMENT_TARGET[arch=ppc64] = 10.5
MACOSX_DEPLOYMENT_TARGET[arch=i386] = 10.4
@@ -14,8 +14,9 @@ GCC_DEBUGGING_SYMBOLS = full
GCC_PRECOMPILE_PREFIX_HEADER = YES
GCC_PREFIX_HEADER = $(SDKROOT)/System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h
GCC_FAST_OBJC_DISPATCH = YES
-GCC_VERSION = 4.0;
-ARCHS = ppc i386
+GCC_ENABLE_PASCAL_STRINGS = NO
+ARCHS = ppc i386 x86_64
+GCC_VERSION = com.apple.compilers.llvm.clang.1_0
// Enable warnings
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
@@ -37,4 +38,13 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES
GCC_WARN_UNUSED_VARIABLE = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES
-WARNING_CFLAGS = -Wall
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
+GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES
+GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES
+GCC_WARN_UNUSED_FUNCTION = YES
+GCC_WARN_UNUSED_LABEL = YES
+GCC_WARN_UNUSED_VALUE = YES
+GCC_WARN_UNUSED_PARAMETER = YES
+GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
+GCC_WARN_UNDECLARED_SELECTOR = YES
+WARNING_CFLAGS = -Wall -Wno-unused-parameter -Werror -Wundef -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-format-attribute -Wpacked -Wredundant-decls -Winline -Wdisabled-optimization -Wformat=2 -Winvalid-pch
View
7 Configurations/ConfigCommonDebug.xcconfig
@@ -3,5 +3,8 @@
GCC_OPTIMIZATION_LEVEL = 0
DEBUG_INFORMATION_FORMAT = dwarf
GCC_GENERATE_DEBUGGING_SYMBOLS = YES
-SPARKLE_EXTRA_DEBUG = -DDEBUG -fstack-protector -D_FORTIFY_SOURCE=2
-OTHER_CFLAGS = $(SPARKLE_EXTRA_DEBUG)
+SPARKLE_EXTRA_DEBUG_10_5_ONLY = -fstack-protector -D_FORTIFY_SOURCE=2
+SPARKLE_EXTRA_DEBUG = -DDEBUG
+OTHER_CFLAGS = $(SPARKLE_EXTRA_DEBUG)
+
+// Add $(SPARKLE_EXTRA_DEBUG_10_5_ONLY) to SPARKLE_EXTRA_DEBUG if your deployment is 10.5 or greater.
View
9 Configurations/ConfigUnitTest.xcconfig
@@ -0,0 +1,9 @@
+// Unit Test only
+
+INFOPLIST_FILE = Tests/Sparkle Unit Tests-Info.plist
+OTHER_LDFLAGS = -framework Cocoa -framework SenTestingKit
+PRODUCT_NAME = Sparkle Unit Tests
+WRAPPER_EXTENSION = octest
+FRAMEWORK_SEARCH_PATHS = $(DEVELOPER_LIBRARY_DIR)/Frameworks
+GCC_PREFIX_HEADER = $(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h
+GCC_PRECOMPILE_PREFIX_HEADER = YES
View
3  Configurations/ConfigUnitTestDebug.xcconfig
@@ -0,0 +1,3 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonDebug.xcconfig"
+#include "ConfigUnitTest.xcconfig"
View
3  Configurations/ConfigUnitTestRelease.xcconfig
@@ -0,0 +1,3 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonRelease.xcconfig"
+#include "ConfigUnitTest.xcconfig"
View
3  Configurations/ConfigUnitTestReleaseGCSupport.xcconfig
@@ -0,0 +1,3 @@
+#include "ConfigUnitTestRelease.xcconfig"
+
+GCC_ENABLE_OBJC_GC = required
View
29 License.txt
@@ -123,4 +123,31 @@ Original SSLeay License
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
-*/
+*/
+
+License for bspatch.c and bsdiff.c, from bsdiff 4.3 (<http://www.daemonology.net/bsdiff/>:
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
View
1  NTSynchronousTask.h
@@ -11,6 +11,7 @@
@interface NTSynchronousTask : NSObject
{
+@private
NSTask *mv_task;
NSPipe *mv_outputPipe;
NSPipe *mv_inputPipe;
View
263 NTSynchronousTask.m
@@ -13,154 +13,8 @@
#import "SUVersionComparisonProtocol.h"
#import "NTSynchronousTask.h"
-@interface NTSynchronousTask (Private)
-- (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
-
-- (NSTask *)task;
-- (void)setTask:(NSTask *)theTask;
-
-- (NSPipe *)outputPipe;
-- (void)setOutputPipe:(NSPipe *)theOutputPipe;
-
-- (NSPipe *)inputPipe;
-- (void)setInputPipe:(NSPipe *)theInputPipe;
-
-- (NSData *)output;
-- (void)setOutput:(NSData *)theOutput;
-
-- (BOOL)done;
-- (void)setDone:(BOOL)flag;
-
-- (int)result;
-- (void)setResult:(int)theResult;
-@end
-
@implementation NTSynchronousTask
-- (id)init;
-{
- self = [super init];
- if (self)
- {
- [self setTask:[[[NSTask alloc] init] autorelease]];
- [self setOutputPipe:[[[NSPipe alloc] init] autorelease]];
- [self setInputPipe:[[[NSPipe alloc] init] autorelease]];
-
- [[self task] setStandardInput:[self inputPipe]];
- [[self task] setStandardOutput:[self outputPipe]];
- [[self task] setStandardError:[self outputPipe]];
- }
-
- return self;
-}
-
-//----------------------------------------------------------
-// dealloc
-//----------------------------------------------------------
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [self setTask:nil];
- [self setOutputPipe:nil];
- [self setInputPipe:nil];
- [self setOutput:nil];
-
- [super dealloc];
-}
-
-+(int) task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input output: (NSData**)outData
-{
- // we need this wacky pool here, otherwise we run out of pipes, the pipes are internally autoreleased
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int taskResult = 0;
- if( outData )
- *outData = nil;
-
- NS_DURING
- {
- NTSynchronousTask* task = [[NTSynchronousTask alloc] init];
-
- [task run:toolPath directory:currentDirectory withArgs:args input:input];
-
- taskResult = [task result];
- if( outData )
- *outData = [[task output] retain];
-
- [task release];
- }
- NS_HANDLER;
- taskResult = errCppGeneral;
- NS_ENDHANDLER;
-
- [pool drain];
-
- // retained above
- if( outData )
- [*outData autorelease];
-
- return taskResult;
-}
-
-@end
-
-@implementation NTSynchronousTask (Private)
-
-- (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
-{
- BOOL success = NO;
-
- if (currentDirectory)
- [[self task] setCurrentDirectoryPath: currentDirectory];
-
- [[self task] setLaunchPath:toolPath];
- [[self task] setArguments:args];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(taskOutputAvailable:)
- name:NSFileHandleReadToEndOfFileCompletionNotification
- object:[[self outputPipe] fileHandleForReading]];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(taskDidTerminate:)
- name:NSTaskDidTerminateNotification
- object:[self task]];
-
- [[[self outputPipe] fileHandleForReading] readToEndOfFileInBackgroundAndNotifyForModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode, nil]];
-
- NS_DURING
- [[self task] launch];
- success = YES;
- NS_HANDLER
- ;
- NS_ENDHANDLER
-
- if (success)
- {
- if (input)
- {
- // feed the running task our input
- [[[self inputPipe] fileHandleForWriting] writeData:input];
- [[[self inputPipe] fileHandleForWriting] closeFile];
- }
-
- // loop until we are done receiving the data
- if (![self done])
- {
- double resolution = 1;
- BOOL isRunning;
- NSDate* next;
-
- do {
- next = [NSDate dateWithTimeIntervalSinceNow:resolution];
-
- isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
- beforeDate:next];
- } while (isRunning && ![self done]);
- }
- }
-}
-
//----------------------------------------------------------
// task
//----------------------------------------------------------
@@ -251,10 +105,6 @@ - (void)setResult:(int)theResult
mv_result = theResult;
}
-@end
-
-@implementation NTSynchronousTask (Notifications)
-
- (void)taskOutputAvailable:(NSNotification*)note
{
[self setOutput:[[note userInfo] objectForKey:NSFileHandleNotificationDataItem]];
@@ -267,6 +117,117 @@ - (void)taskDidTerminate:(NSNotification*)note
[self setResult:[[self task] terminationStatus]];
}
-@end
+- (id)init;
+{
+ self = [super init];
+ if (self)
+ {
+ [self setTask:[[[NSTask alloc] init] autorelease]];
+ [self setOutputPipe:[[[NSPipe alloc] init] autorelease]];
+ [self setInputPipe:[[[NSPipe alloc] init] autorelease]];
+
+ [[self task] setStandardInput:[self inputPipe]];
+ [[self task] setStandardOutput:[self outputPipe]];
+ }
+
+ return self;
+}
+
+//----------------------------------------------------------
+// dealloc
+//----------------------------------------------------------
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [mv_task release];
+ [mv_outputPipe release];
+ [mv_inputPipe release];
+ [mv_output release];
+
+ [super dealloc];
+}
+
+- (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
+{
+ BOOL success = NO;
+
+ if (currentDirectory)
+ [[self task] setCurrentDirectoryPath: currentDirectory];
+
+ [[self task] setLaunchPath:toolPath];
+ [[self task] setArguments:args];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(taskOutputAvailable:)
+ name:NSFileHandleReadToEndOfFileCompletionNotification
+ object:[[self outputPipe] fileHandleForReading]];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(taskDidTerminate:)
+ name:NSTaskDidTerminateNotification
+ object:[self task]];
+
+ [[[self outputPipe] fileHandleForReading] readToEndOfFileInBackgroundAndNotifyForModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode, nil]];
+
+ @try
+ {
+ [[self task] launch];
+ success = YES;
+ }
+ @catch (NSException *localException) { }
+
+ if (success)
+ {
+ if (input)
+ {
+ // feed the running task our input
+ [[[self inputPipe] fileHandleForWriting] writeData:input];
+ [[[self inputPipe] fileHandleForWriting] closeFile];
+ }
+
+ // loop until we are done receiving the data
+ if (![self done])
+ {
+ double resolution = 1;
+ BOOL isRunning;
+ NSDate* next;
+
+ do {
+ next = [NSDate dateWithTimeIntervalSinceNow:resolution];
+
+ isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
+ beforeDate:next];
+ } while (isRunning && ![self done]);
+ }
+ }
+}
++ (NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
+{
+ // we need this wacky pool here, otherwise we run out of pipes, the pipes are internally autoreleased
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSData* result=nil;
+
+ @try
+ {
+ NTSynchronousTask* task = [[NTSynchronousTask alloc] init];
+
+ [task run:toolPath directory:currentDirectory withArgs:args input:input];
+
+ if ([task result] == 0)
+ result = [[task output] retain];
+
+ [task release];
+ }
+ @catch (NSException *localException) { }
+
+ [pool drain];
+
+ // retained above
+ [result autorelease];
+
+ return result;
+}
+@end
View
5 SUAppcast.h
@@ -10,11 +10,14 @@
#define SUAPPCAST_H
@class SUAppcastItem;
-@interface SUAppcast : NSObject {
+@interface SUAppcast : NSObject
+{
+@private
NSArray *items;
NSString *userAgentString;
id delegate;
NSString *downloadFilename;
+ NSURLDownload *download;
}
- (void)fetchAppcastFromURL:(NSURL *)url;
View
100 SUAppcast.m
@@ -15,6 +15,22 @@
#import "SUConstants.h"
#import "SULog.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;
@@ -31,6 +47,8 @@ - (void)dealloc
userAgentString = nil;
[downloadFilename release];
downloadFilename = nil;
+ [download release];
+ download = nil;
[super dealloc];
}
@@ -46,44 +64,50 @@ - (void)fetchAppcastFromURL:(NSURL *)url
if (userAgentString)
[request setValue:userAgentString forHTTPHeaderField:@"User-Agent"];
- NSURLDownload *download = [[[NSURLDownload alloc] initWithRequest:request delegate:self] autorelease];
- CFRetain(download);
+ download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
}
-- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
+- (void)download:(NSURLDownload *)aDownload decideDestinationWithSuggestedFilename:(NSString *)filename
{
- NSString *destinationFilename = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
- [download setDestination:destinationFilename allowOverwrite:NO];
+ NSString* destinationFilename = NSTemporaryDirectory();
+ if (destinationFilename)
+ {
+ destinationFilename = [destinationFilename stringByAppendingPathComponent:filename];
+ [download setDestination:destinationFilename allowOverwrite:NO];
+ }
}
-- (void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path
+- (void)download:(NSURLDownload *)aDownload didCreateDestination:(NSString *)path
{
[downloadFilename release];
downloadFilename = [path copy];
}
-- (void)downloadDidFinish:(NSURLDownload *)download
-{
- CFRelease(download);
-
+- (void)downloadDidFinish:(NSURLDownload *)aDownload
+{
NSError *error = nil;
- NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:downloadFilename] options:0 error:&error];
+
+ NSXMLDocument *document = nil;
BOOL failed = NO;
NSArray *xmlItems = nil;
NSMutableArray *appcastItems = [NSMutableArray array];
-
- #if DEBUG
- NSString* debugXML = [NSString stringWithContentsOfFile: downloadFilename encoding: NSUTF8StringEncoding error: nil];
- SULog(@"<<<< XML >>>>\n%@\n>>>> XML <<<<", debugXML);
- #endif
+
+ if (downloadFilename)
+ {
+ document = [[[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:downloadFilename] options:0 error:&error] autorelease];
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
- [[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
+ [[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
#else
- [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:NULL];
+ [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:nil];
#endif
- [downloadFilename release];
- downloadFilename = nil;
+ [downloadFilename release];
+ downloadFilename = nil;
+ }
+ else
+ {
+ failed = YES;
+ }
if (nil == document)
{
@@ -137,12 +161,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"];
}
@@ -153,19 +172,29 @@ - (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];
}
}
NSString *errString;
- SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict failureReason:&errString];
+ SUAppcastItem *anItem = [[[SUAppcastItem alloc] initWithDictionary:dict failureReason:&errString] autorelease];
if (anItem)
{
[appcastItems addObject:anItem];
- [anItem release];
}
else
{
@@ -175,8 +204,6 @@ - (void)downloadDidFinish:(NSURLDownload *)download
[dict removeAllObjects];
}
}
-
- [document release];
if ([appcastItems count])
{
@@ -195,15 +222,14 @@ - (void)downloadDidFinish:(NSURLDownload *)download
}
}
-- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
+- (void)download:(NSURLDownload *)aDownload didFailWithError:(NSError *)error
{
- CFRelease(download);
if (downloadFilename)
{
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
[[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
#else
- [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:NULL];
+ [[NSFileManager defaultManager] removeItemAtPath:downloadFilename error:nil];
#endif
}
[downloadFilename release];
@@ -212,7 +238,7 @@ - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
[self reportError:error];
}
-- (NSURLRequest *)download:(NSURLDownload *)download willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
+- (NSURLRequest *)download:(NSURLDownload *)aDownload willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
{
return request;
}
@@ -237,7 +263,7 @@ - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes
NSXMLElement *node;
NSMutableArray *languages = [NSMutableArray array];
NSString *lang;
- NSInteger i;
+ NSUInteger i;
while ((node = [nodeEnum nextObject]))
{
lang = [[node attributeForName:@"xml:lang"] stringValue];
View
10 SUAppcastItem.h
@@ -9,7 +9,9 @@
#ifndef SUAPPCASTITEM_H
#define SUAPPCASTITEM_H
-@interface SUAppcastItem : NSObject {
+@interface SUAppcastItem : NSObject
+{
+@private
NSString *title;
NSDate *date;
NSString *itemDescription;
@@ -22,7 +24,9 @@
NSURL *fileURL;
NSString *versionString;
NSString *displayVersionString;
-
+
+ NSDictionary *deltaUpdates;
+
NSDictionary *propertiesDictionary;
NSURL *infoURL; // UK 2007-08-31
@@ -41,6 +45,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
35 SUAppcastItem.m
@@ -116,6 +116,20 @@ - (void)setInfoURL:(NSURL *)aFileURL // UK 2007-08-31 (whole method)
infoURL = [aFileURL 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
{
return [self initWithDictionary:dict failureReason:nil];
@@ -210,6 +224,26 @@ - (void)setInfoURL:(NSURL *)aFileURL // UK 2007-08-31 (whole method)
[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;
}
@@ -221,6 +255,7 @@ - (void)dealloc
[self setItemDescription:nil];
[self setReleaseNotesURL:nil];
[self setDSASignature:nil];
+ [self setMinimumSystemVersion: nil];
[self setFileURL:nil];
[self setVersionString:nil];
[self setDisplayVersionString:nil];
View
4 SUAutomaticUpdateDriver.h
@@ -13,7 +13,9 @@
#import "SUBasicUpdateDriver.h"
@class SUAutomaticUpdateAlert;
-@interface SUAutomaticUpdateDriver : SUBasicUpdateDriver {
+@interface SUAutomaticUpdateDriver : SUBasicUpdateDriver
+{
+@private
BOOL postponingInstallation, showErrors;
SUAutomaticUpdateAlert *alert;
}
View
2  SUBasicUpdateDriver.h
@@ -15,6 +15,7 @@
@class SUAppcastItem, SUUnarchiver, SUAppcast, SUUnarchiver, SUHost;
@interface SUBasicUpdateDriver : SUUpdateDriver {
SUAppcastItem *updateItem;
+ SUAppcastItem *nonDeltaUpdateItem;
NSURLDownload *download;
NSString *downloadPath;
@@ -43,6 +44,7 @@
- (void)extractUpdate;
- (void)unarchiverDidFinish:(SUUnarchiver *)ua;
- (void)unarchiverDidFail:(SUUnarchiver *)ua;
+- (void)failedToApplyDeltaUpdate;
- (void)installUpdate;
- (void)installerForHost:(SUHost *)host failedWithError:(NSError *)error;
View
46 SUBasicUpdateDriver.m
@@ -17,6 +17,7 @@
#import "SULog.h"
#import "SUPlainInstaller.h"
#import "SUPlainInstallerInternals.h"
+#import "SUBinaryDeltaCommon.h"
@implementation SUBasicUpdateDriver
@@ -39,7 +40,7 @@ - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)aHost
[appcast fetchAppcastFromURL:URL];
}
-- (id <SUVersionComparison>)_versionComparator
+- (id <SUVersionComparison>)versionComparator
{
id <SUVersionComparison> comparator = nil;
@@ -56,7 +57,7 @@ - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)aHost
- (BOOL)isItemNewer:(SUAppcastItem *)ui
{
- return [[self _versionComparator] compareVersion:[host version] toVersion:[ui versionString]] == NSOrderedAscending;
+ return [[self versionComparator] compareVersion:[host version] toVersion:[ui versionString]] == NSOrderedAscending;
}
- (BOOL)hostSupportsItem:(SUAppcastItem *)ui
@@ -69,7 +70,7 @@ - (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui
{
NSString *skippedVersion = [host objectForUserDefaultsKey:SUSkippedVersionKey];
if (skippedVersion == nil) { return NO; }
- return [[self _versionComparator] compareVersion:[ui versionString] toVersion:skippedVersion] != NSOrderedDescending;
+ return [[self versionComparator] compareVersion:[ui versionString] toVersion:skippedVersion] != NSOrderedDescending;
}
- (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui
@@ -96,10 +97,18 @@ - (void)appcastDidFinishLoading:(SUAppcast *)ac
do {
item = [updateEnumerator nextObject];
} while (item && ![self hostSupportsItem:item]);
+
+ if (binaryDeltaSupported()) {
+ SUAppcastItem *deltaUpdateItem = [[item deltaUpdates] objectForKey:[host version]];
+ if (deltaUpdateItem && [self hostSupportsItem:deltaUpdateItem]) {
+ nonDeltaUpdateItem = [item retain];
+ item = deltaUpdateItem;
+ }
+ }
}
updateItem = [item retain];
- CFRelease(ac); // Remember that we're explicitly managing the memory of the appcast.
+ if (ac) { CFRelease(ac); } // Remember that we're explicitly managing the memory of the appcast.
if (updateItem == nil) { [self didNotFindUpdate]; return; }
if ([self itemContainsValidUpdate:updateItem])
@@ -110,7 +119,7 @@ - (void)appcastDidFinishLoading:(SUAppcast *)ac
- (void)appcast:(SUAppcast *)ac failedToLoadWithError:(NSError *)error
{
- CFRelease(ac); // Remember that we're explicitly managing the memory of the appcast.
+ if (ac) { CFRelease(ac); } // Remember that we're explicitly managing the memory of the appcast.
[self abortUpdateWithError:error];
}
@@ -206,7 +215,7 @@ - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSS
- (void)extractUpdate
{
- SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath];
+ SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath updatingHost:host];
if (!unarchiver)
{
SULog(@"Sparkle Error: No valid unarchiver for %@!", downloadPath);
@@ -218,6 +227,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); }
@@ -227,6 +246,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]]];
}
@@ -236,10 +261,12 @@ - (void)installUpdate
{
if ([[updater delegate] respondsToSelector:@selector(updater:willInstallUpdate:)])
[[updater delegate] updater:updater willInstallUpdate:updateItem];
+
// Copy the relauncher into a temporary directory so we can get to it after the new version's installed.
- NSString *relaunchPathToCopy = [[NSBundle bundleForClass:[self class]] pathForResource:@"finish_installation" ofType:@"app"];
- NSString *appSupportFolder = [[@"~/Library/Application Support/" stringByExpandingTildeInPath] stringByAppendingPathComponent: [host name]];
- NSString *targetPath = [appSupportFolder stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]];
+ NSString *relaunchPathToCopy = [SPARKLE_BUNDLE pathForResource:@"relaunch" ofType:@""];
+ NSString *targetPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]];
+ // Only the paranoid survive: if there's already a stray copy of relaunch there, we would have problems.
+ NSError *error = nil;
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
[[NSFileManager defaultManager] createDirectoryAtPath: [targetPath stringByDeletingLastPathComponent] attributes: [NSDictionary dictionary]];
#else
@@ -335,6 +362,7 @@ - (void)abortUpdateWithError:(NSError *)error
- (void)dealloc
{
[updateItem release];
+ [nonDeltaUpdateItem release];
[download release];
[downloadPath release];
[tempDir release];
View
15 SUBinaryDeltaApply.h
@@ -0,0 +1,15 @@
+//
+// SUBinaryDeltaApply.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTAAPPLY_H
+#define SUBINARYDELTAAPPLY_H
+
+@class NSString;
+int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile);
+
+#endif
View
101 SUBinaryDeltaApply.m
@@ -0,0 +1,101 @@
+//
+// SUBinaryDeltaApply.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#import "SUBinaryDeltaApply.h"
+#import "SUBinaryDeltaCommon.h"
+#import <CommonCrypto/CommonDigest.h>
+#import <Foundation/Foundation.h>
+#import <bspatch.h>
+#import <stdio.h>
+#import <stdlib.h>
+#import <xar/xar.h>
+
+static void applyBinaryDeltaToFile(xar_t x, xar_file_t file, NSString *sourceFilePath, NSString *destinationFilePath)
+{
+ NSString *patchFile = temporaryFilename(@"apply-binary-delta");
+ xar_extract_tofile(x, file, [patchFile fileSystemRepresentation]);
+ const char *argv[] = {"/usr/bin/bspatch", [sourceFilePath fileSystemRepresentation], [destinationFilePath fileSystemRepresentation], [patchFile fileSystemRepresentation]};
+ bspatch(4, (char **)argv);
+ unlink([patchFile fileSystemRepresentation]);
+}
+
+int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile)
+{
+ xar_t x = xar_open([patchFile UTF8String], READ);
+ if (!x) {
+ fprintf(stderr, "Unable to open %s. Giving up.\n", [patchFile UTF8String]);
+ return 1;
+ }
+
+ NSString *expectedBeforeHash = nil;
+ NSString *expectedAfterHash = nil;
+ xar_subdoc_t subdoc;
+ for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) {
+ if (!strcmp(xar_subdoc_name(subdoc), "binary-delta-attributes")) {
+ const char *value = 0;
+ xar_subdoc_prop_get(subdoc, "before-sha1", &value);
+ if (value)
+ expectedBeforeHash = [NSString stringWithUTF8String:value];
+
+ xar_subdoc_prop_get(subdoc, "after-sha1", &value);
+ if (value)
+ expectedAfterHash = [NSString stringWithUTF8String:value];
+ }
+ }
+
+ if (!expectedBeforeHash || !expectedAfterHash) {
+ fprintf(stderr, "Unable to find before-sha1 or after-sha1 metadata in delta. Giving up.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Verifying source... ");
+ NSString *beforeHash = hashOfTree(source);
+
+ if (![beforeHash isEqualToString:expectedBeforeHash]) {
+ fprintf(stderr, "Source doesn't have expected hash (%s != %s). Giving up.\n", [expectedBeforeHash UTF8String], [beforeHash UTF8String]);
+ return 1;
+ }
+
+ fprintf(stderr, "\nCopying files... ");
+ removeTree(destination);
+ copyTree(source, destination);
+
+ fprintf(stderr, "\nPatching... ");
+ xar_file_t file;
+ xar_iter_t iter = xar_iter_new();
+ for (file = xar_file_first(x, iter); file; file = xar_file_next(iter)) {
+ NSString *path = [NSString stringWithUTF8String:xar_get_path(file)];
+ NSString *sourceFilePath = [source stringByAppendingPathComponent:path];
+ NSString *destinationFilePath = [destination stringByAppendingPathComponent:path];
+
+ const char *value;
+ if (!xar_prop_get(file, "delete", &value) || !xar_prop_get(file, "delete-then-extract", &value)) {
+ removeTree(destinationFilePath);
+ if (!xar_prop_get(file, "delete", &value))
+ continue;
+ }
+
+ if (!xar_prop_get(file, "binary-delta", &value))
+ applyBinaryDeltaToFile(x, file, sourceFilePath, destinationFilePath);
+ else
+ xar_extract_tofile(x, file, [destinationFilePath fileSystemRepresentation]);
+ }
+ xar_close(x);
+
+ fprintf(stderr, "\nVerifying destination... ");
+ NSString *afterHash = hashOfTree(destination);
+
+ if (![afterHash isEqualToString:expectedAfterHash]) {
+ fprintf(stderr, "Destination doesn't have expected hash (%s != %s). Giving up.\n", [expectedAfterHash UTF8String], [afterHash UTF8String]);
+ removeTree(destination);
+ return 1;
+ }
+
+ fprintf(stderr, "\nDone!\n");
+ return 0;
+}
View
26 SUBinaryDeltaCommon.h
@@ -0,0 +1,26 @@
+//
+// SUBinaryDeltaCommon.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTACOMMON_H
+#define SUBINARYDELTACOMMON_H
+
+#include <fts.h>
+
+@class NSString;
+@class NSData;
+
+extern int binaryDeltaSupported(void);
+extern int compareFiles(const FTSENT **a, const FTSENT **b);
+extern NSData *hashOfFile(FTSENT *ent);
+extern NSString *hashOfTree(NSString *path);
+extern void removeTree(NSString *path);
+extern void copyTree(NSString *source, NSString *dest);
+extern NSString *pathRelativeToDirectory(NSString *directory, NSString *path);
+NSString *temporaryFilename(NSString *base);
+
+#endif
View
165 SUBinaryDeltaCommon.m
@@ -0,0 +1,165 @@
+//
+// SUBinaryDeltaCommon.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#include "SUBinaryDeltaCommon.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <Foundation/Foundation.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+extern int xar_close(void*) __attribute__((weak_import));
+
+int binaryDeltaSupported(void)
+{
+ // OS X 10.4 didn't include libxar, so we link against it weakly.
+ // This checks whether libxar is available at runtime.
+ return xar_close != 0;
+}
+
+int compareFiles(const FTSENT **a, const FTSENT **b)
+{
+ return strcoll((*a)->fts_name, (*b)->fts_name);
+}
+
+NSString *pathRelativeToDirectory(NSString *directory, NSString *path)
+{
+ NSUInteger directoryLength = [directory length];
+ if ([path hasPrefix:directory])
+ return [path substringFromIndex:directoryLength];
+
+ return path;
+}
+
+NSString *temporaryFilename(NSString *base)
+{
+ NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]];
+ char buffer[MAXPATHLEN];
+ strcpy(buffer, [template fileSystemRepresentation]);
+ return [NSString stringWithUTF8String:mktemp(buffer)];
+}
+
+static void _hashOfBuffer(unsigned char *hash, const char* buffer, size_t bufferLength)
+{
+ assert(bufferLength <= UINT32_MAX);
+ CC_SHA1_CTX hashContext;
+ CC_SHA1_Init(&hashContext);
+ CC_SHA1_Update(&hashContext, buffer, (CC_LONG)bufferLength);
+ CC_SHA1_Final(hash, &hashContext);
+}
+
+static void _hashOfFile(unsigned char* hash, FTSENT *ent)
+{
+ if (ent->fts_info == FTS_SL) {
+ char linkDestination[MAXPATHLEN + 1];
+ ssize_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN);
+ if (linkDestinationLength < 0) {
+ perror("readlink");
+ return;
+ }
+
+ _hashOfBuffer(hash, linkDestination, linkDestinationLength);
+ return;
+ }
+
+ if (ent->fts_info == FTS_F) {
+ int fileDescriptor = open(ent->fts_path, O_RDONLY);
+ if (fileDescriptor == -1) {
+ perror("open");
+ return;
+ }
+
+ size_t fileSize = (size_t)ent->fts_statp->st_size;
+ if (fileSize == 0) {
+ _hashOfBuffer(hash, NULL, 0);
+ close(fileDescriptor);
+ return;
+ }
+
+ void *buffer = mmap(0, fileSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fileDescriptor, 0);
+ if (buffer == (void*)-1) {
+ close(fileDescriptor);
+ perror("mmap");
+ return;
+ }
+
+ _hashOfBuffer(hash, buffer, fileSize);
+ munmap(buffer, fileSize);
+ close(fileDescriptor);
+ return;
+ }
+
+ if (ent->fts_info == FTS_D)
+ memset(hash, 0xdd, CC_SHA1_DIGEST_LENGTH);
+}
+
+NSData *hashOfFile(FTSENT *ent)
+{
+ unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
+ _hashOfFile(fileHash, ent);
+ return [NSData dataWithBytes:fileHash length:CC_SHA1_DIGEST_LENGTH];
+}
+
+NSString *hashOfTree(NSString *path)
+{
+ const char *sourcePaths[] = {[path UTF8String], 0};
+ FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ perror("fts_open");
+ return nil;
+ }
+
+ CC_SHA1_CTX hashContext;
+ CC_SHA1_Init(&hashContext);
+
+ FTSENT *ent = 0;
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
+ continue;
+
+ unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
+ _hashOfFile(fileHash, ent);
+ CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash));
+
+ NSString *relativePath = pathRelativeToDirectory(path, [NSString stringWithUTF8String:ent->fts_path]);
+ NSData *relativePathBytes = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
+ CC_SHA1_Update(&hashContext, [relativePathBytes bytes], (uint32_t)[relativePathBytes length]);
+ }
+ fts_close(fts);
+
+ unsigned char hash[CC_SHA1_DIGEST_LENGTH];
+ CC_SHA1_Final(hash, &hashContext);
+
+ char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1];
+ size_t i;
+ for (i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
+ sprintf(hexHash + i * 2, "%02x", hash[i]);
+
+ return [NSString stringWithUTF8String:hexHash];
+}
+
+void removeTree(NSString *path)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
+ [[NSFileManager defaultManager] removeItemAtPath:path error:0];
+#else
+ [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
+#endif
+}
+
+void copyTree(NSString *source, NSString *dest)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
+ [[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:0];
+#else
+ [[NSFileManager defaultManager] copyPath:source toPath:dest handler:nil];
+#endif
+}
View
269 SUBinaryDeltaTool.m
@@ -0,0 +1,269 @@
+//
+// SUBinaryDeltaTool.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#define _DARWIN_NO_64_BIT_INODE 1
+
+#include "SUBinaryDeltaCommon.h"
+#include "SUBinaryDeltaApply.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <Foundation/Foundation.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xar/xar.h>
+
+extern int bsdiff(int argc, const char **argv);
+
+@interface CreateBinaryDeltaOperation : NSOperation
+{
+ NSString *_relativePath;
+ NSString *_fromPath;
+ NSString *_toPath;
+ NSString *_resultPath;
+}
+- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree;
+
+- (NSString *)relativePath;
+- (NSString *)resultPath;
+@end
+
+@implementation CreateBinaryDeltaOperation
+
+- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree
+{
+ if ((self = [super init])) {
+ _relativePath = [relativePath copy];
+ _fromPath = [[oldTree stringByAppendingPathComponent:relativePath] retain];
+ _toPath = [[newTree stringByAppendingPathComponent:relativePath] retain];
+ }
+ return self;
+}
+
+- (NSString *)relativePath
+{
+ return [[_relativePath retain] autorelease];
+}
+
+- (NSString *)resultPath
+{
+ return [[_resultPath retain] autorelease];
+}
+
+- (void)main
+{
+ NSString *temporaryFile = temporaryFilename(@"BinaryDelta");
+ const char *argv[] = {"/usr/bin/bsdiff", [_fromPath fileSystemRepresentation], [_toPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]};
+ int result = bsdiff(4, argv);
+ if (!result)
+ _resultPath = [temporaryFile retain];
+}
+
+@end
+
+static NSDictionary *infoForFile(FTSENT *ent)
+{
+ NSData *hash = hashOfFile(ent);
+ NSNumber *size = nil;
+ if (ent->fts_info != FTS_D)
+ size = [NSNumber numberWithUnsignedLongLong:ent->fts_statp->st_size];
+ return [NSDictionary dictionaryWithObjectsAndKeys:hash, @"hash", [NSNumber numberWithUnsignedShort:ent->fts_info], @"type", size, @"size", nil];
+}
+
+static NSString *absolutePath(NSString *path)
+{
+ NSURL *url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
+ return [[url absoluteURL] path];
+}
+
+static NSString *temporaryPatchFile(NSString *patchFile)
+{
+ NSString *path = absolutePath(patchFile);
+ NSString *directory = [path stringByDeletingLastPathComponent];
+ NSString *file = [path lastPathComponent];
+ return [NSString stringWithFormat:@"%@/.%@.tmp", directory, file];
+}
+
+static BOOL shouldSkipDeltaCompression(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
+{
+ unsigned long long fileSize = [[newInfo objectForKey:@"size"] unsignedLongLongValue];
+ if (fileSize < 4096)
+ return YES;
+
+ if (!originalInfo)
+ return YES;
+
+ if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
+ return YES;
+
+ return NO;
+}
+
+static BOOL shouldDeleteThenExtract(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
+{
+ if (!originalInfo)
+ return NO;
+
+ if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
+ return YES;
+
+ return NO;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 5) {
+usage:
+ fprintf(stderr, "Usage: BinaryDelta [create | apply] before-tree after-tree patch-file\n");
+ exit(1);
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *command = [NSString stringWithUTF8String:argv[1]];
+ NSString *oldPath = [NSString stringWithUTF8String:argv[2]];
+ NSString *newPath = [NSString stringWithUTF8String:argv[3]];
+ NSString *patchFile = [NSString stringWithUTF8String:argv[4]];
+
+ if ([command isEqualToString:@"apply"]) {
+ int result = applyBinaryDelta(oldPath, newPath, patchFile);
+ [pool drain];
+ return result;
+ }
+ if (![command isEqualToString:@"create"]) {
+ [pool drain];
+ goto usage;
+ }
+
+ NSMutableDictionary *originalTreeState = [NSMutableDictionary dictionary];
+
+ const char *sourcePaths[] = {[oldPath fileSystemRepresentation], 0};
+ FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ [pool drain];
+ perror("fts_open");
+ return 1;
+ }
+
+ fprintf(stderr, "Processing %s...", [oldPath UTF8String]);
+ FTSENT *ent = 0;
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
+ continue;
+
+ NSString *key = pathRelativeToDirectory(oldPath, [NSString stringWithUTF8String:ent->fts_path]);
+ if (![key length])
+ continue;
+
+ NSDictionary *info = infoForFile(ent);
+ [originalTreeState setObject:info forKey:key];
+ }
+ fts_close(fts);
+
+ NSString *beforeHash = hashOfTree(oldPath);
+
+ NSMutableDictionary *newTreeState = [NSMutableDictionary dictionary];
+ for (NSString *key in originalTreeState)
+ {
+ [newTreeState setObject:[NSNull null] forKey:key];
+ }
+
+ fprintf(stderr, "\nProcessing %s... ", [newPath UTF8String]);
+ sourcePaths[0] = [newPath fileSystemRepresentation];
+ fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ [pool drain];
+ perror("fts_open");
+ return 1;
+ }
+
+
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
+ continue;
+
+ NSString *key = pathRelativeToDirectory(newPath, [NSString stringWithUTF8String:ent->fts_path]);
+ if (![key length])
+ continue;
+
+ NSDictionary *info = infoForFile(ent);
+ NSDictionary *oldInfo = [originalTreeState objectForKey:key];
+
+ if ([info isEqual:oldInfo])
+ [newTreeState removeObjectForKey:key];
+ else
+ [newTreeState setObject:info forKey:key];
+ }
+ fts_close(fts);
+
+ NSString *afterHash = hashOfTree(newPath);
+
+ fprintf(stderr, "\nGenerating delta... ");
+
+ NSString *temporaryFile = temporaryPatchFile(patchFile);
+ xar_t x = xar_open([temporaryFile fileSystemRepresentation], WRITE);
+ xar_opt_set(x, XAR_OPT_COMPRESSION, "bzip2");
+ xar_subdoc_t attributes = xar_subdoc_new(x, "binary-delta-attributes");
+ xar_subdoc_prop_set(attributes, "before-sha1", [beforeHash UTF8String]);
+ xar_subdoc_prop_set(attributes, "after-sha1", [afterHash UTF8String]);
+
+ NSOperationQueue *deltaQueue = [[NSOperationQueue alloc] init];
+ NSMutableArray *deltaOperations = [NSMutableArray array];
+
+ NSArray *keys = [[newTreeState allKeys] sortedArrayUsingSelector:@selector(compare:)];
+ for (NSString* key in keys) {
+ id value = [newTreeState valueForKey:key];
+
+ if ([value isEqual:[NSNull null]]) {
+ xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1);
+ assert(newFile);
+ xar_prop_set(newFile, "delete", "true");
+ continue;
+ }
+
+ NSDictionary *originalInfo = [originalTreeState objectForKey:key];
+ NSDictionary *newInfo = [newTreeState objectForKey:key];
+ if (shouldSkipDeltaCompression(key, originalInfo, newInfo)) {
+ NSString *path = [newPath stringByAppendingPathComponent:key];
+ xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]);
+ assert(newFile);
+ if (shouldDeleteThenExtract(key, originalInfo, newInfo))
+ xar_prop_set(newFile, "delete-then-extract", "true");
+ } else {
+ CreateBinaryDeltaOperation *operation = [[CreateBinaryDeltaOperation alloc] initWithRelativePath:key oldTree:oldPath newTree:newPath];
+ [deltaQueue addOperation:operation];
+ [deltaOperations addObject:operation];
+ [operation release];
+ }
+ }
+
+ [deltaQueue waitUntilAllOperationsAreFinished];
+ [deltaQueue release];
+
+ for (CreateBinaryDeltaOperation *operation in deltaOperations) {
+ NSString *resultPath = [operation resultPath];
+ xar_file_t newFile = xar_add_frompath(x, 0, [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]);
+ assert(newFile);
+ xar_prop_set(newFile, "binary-delta", "true");
+ unlink([resultPath fileSystemRepresentation]);
+ }
+
+ xar_close(x);
+
+ unlink([patchFile fileSystemRepresentation]);
+ link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]);
+ unlink([temporaryFile fileSystemRepresentation]);
+ fprintf(stderr, "Done!\n");
+
+ [pool drain];
+ return 0;
+}
View
20 SUBinaryDeltaUnarchiver.h
@@ -0,0 +1,20 @@
+//
+// SUBinaryDeltaUnarchiver.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-03.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTAUNARCHIVER_H
+#define SUBINARYDELTAUNARCHIVER_H
+
+#import <Cocoa/Cocoa.h>
+#import "SUUnarchiver.h"
+
+@interface SUBinaryDeltaUnarchiver : SUUnarchiver {
+}
+
+@end
+
+#endif
View
48 SUBinaryDeltaUnarchiver.m
@@ -0,0 +1,48 @@
+//
+// SUBinaryDeltaUnarchiver.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-03.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#import "SUBinaryDeltaCommon.h"
+#import "SUBinaryDeltaUnarchiver.h"
+#import "SUBinaryDeltaApply.h"
+#import "SUUnarchiver_Private.h"
+#import "SUHost.h"
+#import "NTSynchronousTask.h"
+
+@implementation SUBinaryDeltaUnarchiver
+
++ (BOOL)canUnarchivePath:(NSString *)path
+{
+ return binaryDeltaSupported() && [[path pathExtension] isEqualToString:@"delta"];
+}
+
+- (void)applyBinaryDelta
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSString *sourcePath = [[updateHost bundle] bundlePath];
+ NSString *targetPath = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]];
+
+ int result = applyBinaryDelta(sourcePath, targetPath, archivePath);
+ if (!result)
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
+ else
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
+
+ [pool drain];
+}
+
+- (void)start
+{
+ [NSThread detachNewThreadSelector:@selector(applyBinaryDelta) toTarget:self withObject:nil];
+}
+
++ (void)load
+{
+ [self registerImplementation:self];
+}
+
+@end
View
40 SUConstants.h
@@ -30,37 +30,37 @@
// Notifications:
// -----------------------------------------------------------------------------
-extern NSString *SUUpdaterWillRestartNotification;
+extern NSString *const SUUpdaterWillRestartNotification;
-extern NSString *SUTechnicalErrorInformationKey;
+extern NSString *const SUTechnicalErrorInformationKey;
// -----------------------------------------------------------------------------
// PList keys::
// -----------------------------------------------------------------------------
-extern NSString *SUFeedURLKey;
-extern NSString *SUHasLaunchedBeforeKey;
-extern NSString *SUShowReleaseNotesKey;
-extern NSString *SUSkippedVersionKey;
-extern NSString *SUScheduledCheckIntervalKey;
-extern NSString *SULastCheckTimeKey;
-extern NSString *SUPublicDSAKeyKey;
-extern NSString *SUPublicDSAKeyFileKey;
-extern NSString *SUAutomaticallyUpdateKey;
-extern NSString *SUAllowsAutomaticUpdatesKey;
-extern NSString *SUEnableAutomaticChecksKey;
-extern NSString *SUEnableAutomaticChecksKeyOld;
-extern NSString *SUEnableSystemProfilingKey;
-extern NSString *SUSendProfileInfoKey;
-extern NSString *SULastProfileSubmitDateKey;
-extern NSString *SUFixedHTMLDisplaySizeKey;
-extern NSString *SUKeepDownloadOnFailedInstallKey;
+extern NSString *const SUFeedURLKey;
+extern NSString *const SUHasLaunchedBeforeKey;
+extern NSString *const SUShowReleaseNotesKey;
+extern NSString *const SUSkippedVersionKey;
+extern NSString *const SUScheduledCheckIntervalKey;
+extern NSString *const SULastCheckTimeKey;
+extern NSString *const SUPublicDSAKeyKey;
+extern NSString *const SUPublicDSAKeyFileKey;
+extern NSString *const SUAutomaticallyUpdateKey;
+extern NSString *const SUAllowsAutomaticUpdatesKey;
+extern NSString *const SUEnableAutomaticChecksKey;
+extern NSString *const SUEnableAutomaticChecksKeyOld;
+extern NSString *const SUEnableSystemProfilingKey;
+extern NSString *const SUSendProfileInfoKey;
+extern NSString *const SULastProfileSubmitDateKey;
+extern NSString *const SUFixedHTMLDisplaySizeKey;
+extern NSString *const SUKeepDownloadOnFailedInstallKey;
// -----------------------------------------------------------------------------
// Errors:
// -----------------------------------------------------------------------------
-extern NSString *SUSparkleErrorDomain;
+extern NSString *const SUSparkleErrorDomain;
// Appcast phase errors.
extern OSStatus SUAppcastParseError;
extern OSStatus SUNoUpdateError;
View
46 SUConstants.m
@@ -13,29 +13,29 @@
#import "SUVersionComparisonProtocol.h"
#import "SUConstants.h"
-NSString *SUUpdaterWillRestartNotification = @"SUUpdaterWillRestartNotificationName";
-NSString *SUTechnicalErrorInformationKey = @"SUTechnicalErrorInformation";
-
-NSString *SUHasLaunchedBeforeKey = @"SUHasLaunchedBefore";
-NSString *SUFeedURLKey = @"SUFeedURL";
-NSString *SUShowReleaseNotesKey = @"SUShowReleaseNotes";
-NSString *SUSkippedVersionKey = @"SUSkippedVersion";
-NSString *SUScheduledCheckIntervalKey = @"SUScheduledCheckInterval";
-NSString *SULastCheckTimeKey = @"SULastCheckTime";
-NSString *SUExpectsDSASignatureKey = @"SUExpectsDSASignature";
-NSString *SUPublicDSAKeyKey = @"SUPublicDSAKey";
-NSString *SUPublicDSAKeyFileKey = @"SUPublicDSAKeyFile";
-NSString *SUAutomaticallyUpdateKey = @"SUAutomaticallyUpdate";
-NSString *SUAllowsAutomaticUpdatesKey = @"SUAllowsAutomaticUpdates";
-NSString *SUEnableSystemProfilingKey = @"SUEnableSystemProfiling";
-NSString *SUEnableAutomaticChecksKey = @"SUEnableAutomaticChecks";
-NSString *SUEnableAutomaticChecksKeyOld = @"SUCheckAtStartup";
-NSString *SUSendProfileInfoKey = @"SUSendProfileInfo";
-NSString *SULastProfileSubmitDateKey = @"SULastProfileSubmissionDate";
-NSString *SUFixedHTMLDisplaySizeKey = @"SUFixedHTMLDisplaySize";
-NSString *SUKeepDownloadOnFailedInstallKey = @"SUKeepDownloadOnFailedInstall";
-
-NSString *SUSparkleErrorDomain = @"SUSparkleErrorDomain";
+NSString *const SUUpdaterWillRestartNotification = @"SUUpdaterWillRestartNotificationName";
+NSString *const SUTechnicalErrorInformationKey = @"SUTechnicalErrorInformation";
+
+NSString *const SUHasLaunchedBeforeKey = @"SUHasLaunchedBefore";
+NSString *const SUFeedURLKey = @"SUFeedURL";
+NSString *const SUShowReleaseNotesKey = @"SUShowReleaseNotes";
+NSString *const SUSkippedVersionKey = @"SUSkippedVersion";
+NSString *const SUScheduledCheckIntervalKey = @"SUScheduledCheckInterval";
+NSString *const SULastCheckTimeKey = @"SULastCheckTime";
+NSString *const SUExpectsDSASignatureKey = @"SUExpectsDSASignature";
+NSString *const SUPublicDSAKeyKey = @"SUPublicDSAKey";
+NSString *const SUPublicDSAKeyFileKey = @"SUPublicDSAKeyFile";
+NSString *const SUAutomaticallyUpdateKey = @"SUAutomaticallyUpdate";
+NSString *const SUAllowsAutomaticUpdatesKey = @"SUAllowsAutomaticUpdates";
+NSString *const SUEnableSystemProfilingKey = @"SUEnableSystemProfiling";
+NSString *const SUEnableAutomaticChecksKey = @"SUEnableAutomaticChecks";
+NSString *const SUEnableAutomaticChecksKeyOld = @"SUCheckAtStartup";
+NSString *const SUSendProfileInfoKey = @"SUSendProfileInfo";
+NSString *const SULastProfileSubmitDateKey = @"SULastProfileSubmissionDate";
+NSString *const SUFixedHTMLDisplaySizeKey = @"SUFixedHTMLDisplaySize";
+NSString *const SUKeepDownloadOnFailedInstallKey = @"SUKeepDownloadOnFailedInstall";
+
+NSString *const SUSparkleErrorDomain = @"SUSparkleErrorDomain";
OSStatus SUAppcastParseError = 1000;
OSStatus SUNoUpdateError = 1001;
OSStatus SUAppcastError = 1002;
View
57 SUDSAVerifier.m
@@ -19,11 +19,11 @@
#import <openssl/sha.h>
// Alex: Avoid prototype warning
-long b64decode(unsigned char* str);
-EVP_PKEY* load_dsa_key(char *key);
+static long b64decode(unsigned char* str);
+static EVP_PKEY* load_dsa_key(char *key);
-long b64decode(unsigned char* str)
+static long b64decode(unsigned char* str)
{
unsigned char *cur, *start;
int d, dlast, phase;
@@ -47,12 +47,12 @@ long b64decode(unsigned char* str)
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
};
- d = dlast = phase = 0;
+ dlast = phase = 0;
start = str;
for (cur = str; *cur != '\0'; ++cur )
{
if(*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
- d = table[(int)*cur];
+ d = table[(unsigned int)*cur];
if(d != -1)
{
switch(phase)
@@ -83,26 +83,30 @@ long b64decode(unsigned char* str)
return str - start;
}
-EVP_PKEY* load_dsa_key(char *key)
+static EVP_PKEY* load_dsa_key(char *key)
{
EVP_PKEY* pkey = NULL;
- BIO *bio;
- if((bio = BIO_new_mem_buf(key, (int)strlen(key))))
+ size_t keylen = strlen(key);
+ if (keylen <= INT_MAX)
{
- DSA* dsa_key = 0;
- if(PEM_read_bio_DSA_PUBKEY(bio, &dsa_key, NULL, NULL))
+ BIO *bio;
+ if((bio = BIO_new_mem_buf(key, (int)keylen)))
{
- if((pkey = EVP_PKEY_new()))
+ DSA* dsa_key = 0;
+ if(PEM_read_bio_DSA_PUBKEY(bio, &dsa_key, NULL, NULL))
{
- if(EVP_PKEY_assign_DSA(pkey, dsa_key) != 1)
+ if((pkey = EVP_PKEY_new()))
{
- DSA_free(dsa_key);
- EVP_PKEY_free(pkey);
- pkey = NULL;
+ if(EVP_PKEY_assign_DSA(pkey, dsa_key) != 1)
+ {
+ DSA_free(dsa_key);
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
}
}
+ BIO_free(bio);
}
- BIO_free(bio);
}
return pkey;
}
@@ -125,13 +129,17 @@ + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encode
}
pkeyString = [pkeyTrimmedLines componentsJoinedByString:@"\n"]; // Put them back together.
+ NSMutableData* pkeyData = [[[pkeyString dataUsingEncoding:NSUTF8StringEncoding] mutableCopy] autorelease];
+ void *pkeyBytes = [pkeyData mutableBytes];
EVP_PKEY* pkey = NULL;
- pkey = load_dsa_key((char *)[pkeyString UTF8String]);
+ pkey = load_dsa_key(pkeyBytes);
if (!pkey) { return NO; }
// Now, the signature is in base64; we have to decode it into a binary stream.
- unsigned char *signature = (unsigned char *)[encodedSignature UTF8String];
+ NSMutableData* signatureData = [[[encodedSignature dataUsingEncoding:NSUTF8StringEncoding] mutableCopy] autorelease];
+ void *signature = [signatureData mutableBytes];
long length = b64decode(signature); // Decode the signature in-place and get the new length of the signature string.
+ if ((length < 0) || (length > 0x7FFFFFFF)) { return NO; } // test before cast below
// We've got the signature, now get the file data.
NSData *pathData = [NSData dataWithContentsOfFile:path];
@@ -143,14 +151,23 @@ + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encode
// Actually verify the signature on the file.
EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
if(EVP_VerifyInit(&ctx, EVP_dss1()) == 1) // We're using DSA keys.
{
EVP_VerifyUpdate(&ctx, md, SHA_DIGEST_LENGTH);
- result = (EVP_VerifyFinal(&ctx, signature, (unsigned int)length, pkey) == 1);
+
+ result = EVP_VerifyFinal(&ctx, signature, (unsigned int)length, pkey);
}
+ EVP_MD_CTX_cleanup(&ctx);
EVP_PKEY_free(pkey);
- return result;
+
+ // Prevent these from being collected earlier than we want (our only reference is an inner pointer).
+ [pkeyData self];
+ [signatureData self];
+ [pathData self];
+
+ return result == 1;
}
@end
View
40 SUDiskImageUnarchiver.m
@@ -15,17 +15,12 @@
@implementation SUDiskImageUnarchiver
-+ (BOOL)_canUnarchivePath:(NSString *)path
++ (BOOL)canUnarchivePath:(NSString *)path
{
return [[path pathExtension] isEqualToString:@"dmg"];
}
-- (void)start
-{
- [NSThread detachNewThreadSelector:@selector(_extractDMG) toTarget:self withObject:nil];
-}
-
-- (void)_extractDMG
+- (void)extractDMG
{
// GETS CALLED ON NON-MAIN THREAD!!!
@@ -41,14 +36,16 @@ - (void)_extractDMG
do
{
CFUUIDRef uuid = CFUUIDCreate(NULL);
- mountPointName = (NSString *)CFUUIDCreateString(NULL, uuid);
- CFRelease(uuid);
-#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
- [NSMakeCollectable((CFStringRef)mountPointName) autorelease];
-#else
- [mountPointName autorelease];
-#endif
- mountPoint = [@"/Volumes" stringByAppendingPathComponent:mountPointName];
+ if (uuid)
+ {
+ CFStringRef uuidString = CFUUIDCreateString(NULL, uuid);
+ if (uuidString)
+ {
+ mountPoint = [@"/Volumes" stringByAppendingPathComponent:(NSString*)uuidString];
+ CFRelease(uuidString);
+ }
+ CFRelease(uuid);
+ }
}
while (noErr == FSPathMakeRefWithOptions((UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL));
@@ -69,7 +66,7 @@ - (void)_extractDMG
// Now that we've mounted it, we need to copy out its contents.
FSRef srcRef, dstRef;
- OSErr err;
+ OSStatus err;
err = FSPathMakeRef((UInt8 *)[mountPoint fileSystemRepresentation], &srcRef, NULL);
if (err != noErr) goto reportError;
err = FSPathMakeRef((UInt8 *)[[archivePath stringByDeletingLastPathComponent] fileSystemRepresentation], &dstRef, NULL);
@@ -78,11 +75,11 @@ - (void)_extractDMG
err = FSCopyObjectSync(&srcRef, &dstRef, (CFStringRef)mountPointName, NULL, kFSFileOperationSkipSourcePermissionErrors);
if (err != noErr) goto reportError;
- [self performSelectorOnMainThread:@selector(_notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
goto finally;
reportError:
- [self performSelectorOnMainThread:@selector(_notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
finally:
if (mountedSuccessfully)
@@ -92,9 +89,14 @@ - (void)_extractDMG
[pool drain];
}
+- (void)start
+{
+ [NSThread detachNewThreadSelector:@selector(extractDMG) toTarget:self withObject:nil];
+}
+
+ (void)load
{
- [self _registerImplementation:self];
+ [self registerImplementation:self];
}
@end
View
1  SUHost.h
@@ -12,6 +12,7 @@
@interface SUHost : NSObject
{
+@private
NSBundle *bundle;
}
View
26 SUHost.m
@@ -18,9 +18,9 @@ @implementation SUHost
- (id)initWithBundle:(NSBundle *)aBundle
{
- if (aBundle == nil) aBundle = [NSBundle mainBundle];
if ((self = [super init]))
{
+ if (aBundle == nil) aBundle = [NSBundle mainBundle];
bundle = [aBundle retain];
if (![bundle bundleIdentifier])
SULog(@"Sparkle Error: the bundle being updated at %@ has no CFBundleIdentifier! This will cause preference read/write to not work properly.", bundle);
@@ -96,7 +96,17 @@ - (NSImage *)icon
iconPath = [bundle pathForResource:[bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType: nil];
NSImage *icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
// Use a default icon if none is defined.
- if (!icon) { icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(bundle == [NSBundle mainBundle] ? kGenericApplicationIcon : UTGetOSTypeFromString(CFSTR("BNDL")))]; }
+ if (!icon) {
+ BOOL isMainBundle = (bundle == [NSBundle mainBundle]);
+
+ // Starting with 10.6, iconForFileType: accepts a UTI.
+ NSString *fileType = nil;
+ if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5)
+ fileType = isMainBundle ? NSFileTypeForHFSTypeCode(kGenericApplicationIcon) : @".bundle";
+ else
+ fileType = isMainBundle ? (NSString*)kUTTypeApplication : (NSString*)kUTTypeBundle;
+ icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType];
+ }
return icon;
}
@@ -111,8 +121,8 @@ - (BOOL)isBackgroundApplication
{
ProcessSerialNumber PSN;
GetCurrentProcess(&PSN);
- NSDictionary * processInfo = (NSDictionary *)ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
- BOOL isElement = [[processInfo objectForKey:@"LSUIElement"] boolValue];
+ CFDictionaryRef processInfo = ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
+ BOOL isElement = [[(NSDictionary *)processInfo objectForKey:@"LSUIElement"] boolValue];
if (processInfo)
CFRelease(processInfo);
return isElement;
@@ -148,18 +158,14 @@ - (BOOL)boolForInfoDictionaryKey:(NSString *)key
- (id)objectForUserDefaultsKey:(NSString *)defaultName
{
- // Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistratioDomain, so anything
+ // Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistrationDomain, so anything
// passed into -[NSUserDefaults registerDefaults:] is ignored. The following line falls
// back to using NSUserDefaults, but only if the host bundle is the main bundle.
if (bundle == [NSBundle mainBundle])
return [[NSUserDefaults standardUserDefaults] objectForKey:defaultName];
CFPropertyListRef obj = ThreadSafePreferences_CopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]);
-#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
- return [NSMakeCollectable(obj) autorelease];
-#else
- return [(id)obj autorelease];
-#endif
+ return [(id)CFMakeCollectable(obj) autorelease];
}
- (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName;
View
5 SUInstaller.h
@@ -15,10 +15,7 @@
@class SUHost;
@interface SUInstaller : NSObject { }
+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator;
-+ (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate;
-
-+ (NSString*)updateFolder;
-+ (void)notifyDelegateOfFailure: (NSDictionary*)dict;
++ (void)finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate;
@end
@interface NSObject (SUInstallerDelegateInformalProtocol)
View
16 SUInstaller.m
@@ -23,7 +23,7 @@ +(NSString*) updateFolder
return sUpdateFolder;
}
-+ (BOOL)_isAliasFolderAtPath:(NSString *)path
++ (BOOL)isAliasFolderAtPath:(NSString *)path
{
FSRef fileRef;
OSStatus err = noErr;
@@ -95,7 +95,7 @@ + (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)ho
}
// Some DMGs have symlinks into /Applications! That's no good!
- if ([self _isAliasFolderAtPath:currentPath])
+ if ([self isAliasFolderAtPath:currentPath])
[dirEnum skipDescendents];
}
@@ -109,7 +109,7 @@ + (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)ho
if (newAppDownloadPath == nil)
{
- [self _finishInstallationWithResult:NO host:host error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find an appropriate update in the downloaded package." forKey:NSLocalizedDescriptionKey]] delegate:delegate];
+ [self finishInstallationWithResult:NO host:host error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find an appropriate update in the downloaded package." forKey:NSLocalizedDescriptionKey]] delegate:delegate];
}
else
{
@@ -117,7 +117,7 @@ + (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)ho
}
}
-+ (void)_mdimportHost:(SUHost *)host
++ (void)mdimportHost:(SUHost *)host
{
// *** GETS CALLED ON NON-MAIN THREAD!
@@ -143,13 +143,11 @@ + (void)_mdimportHost:(SUHost *)host
#define SUNotifyDictErrorKey @"SUNotifyDictError"
#define SUNotifyDictDelegateKey @"SUNotifyDictDelegate"
-+ (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate
++ (void)finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate
{
- // *** GETS CALLED ON NON-MAIN THREAD!
-
- if (result == YES)
+ if (result)
{
- [self _mdimportHost:host];
+ [self mdimportHost:host];
if ([delegate respondsToSelector:@selector(installerFinishedForHost:)])
[delegate performSelectorOnMainThread: @selector(installerFinishedForHost:) withObject: host waitUntilDone: NO];
}
View
20 SUPackageInstaller.m
@@ -10,10 +10,6 @@
#import <Cocoa/Cocoa.h>
#import "SUConstants.h"
-#ifndef NSAppKitVersionNumber10_4
-#define NSAppKitVersionNumber10_4 824
-#endif
-
NSString *SUPackageInstallerCommandKey = @"SUPackageInstallerCommand";
NSString *SUPackageInstallerArgumentsKey = @"SUPackageInstallerArguments";
NSString *SUPackageInstallerHostKey = @"SUPackageInstallerHost";
@@ -21,12 +17,12 @@
@implementation SUPackageInstaller
-+ (void)_finishInstallationWithInfo:(NSDictionary *)info
++ (void)finishInstallationWithInfo:(NSDictionary *)info
{
- [self _finishInstallationWithResult:YES host:[info objectForKey:SUPackageInstallerHostKey] error:nil delegate:[info objectForKey:SUPackageInstallerDelegateKey]];
+ [self finishInstallationWithResult:YES host:[info objectForKey:SUPackageInstallerHostKey] error:nil delegate:[info objectForKey:SUPackageInstallerDelegateKey]];
}
-+ (void)_performInstallationWithInfo:(NSDictionary *)info
++ (void)performInstallationWithInfo:(NSDictionary *)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@@ -34,7 +30,7 @@ + (void)_performInstallationWithInfo:(NSDictionary *)info
[installer waitUntilExit];
// Known bug: if the installation fails or is canceled, Sparkle goes ahead and restarts, thinking everything is fine.
- [self performSelectorOnMainThread:@selector(_finishInstallationWithInfo:) withObject:info waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(finishInstallationWithInfo:) withObject:info waitUntilDone:NO];
[pool drain];
}
@@ -46,7 +42,7 @@ + (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegat
if (floor(NSAppKitVersionNumber) == NSAppKitVersionNumber10_4) {
// 10.4 uses Installer.app because the "open" command in 10.4 doesn't support -W and -n
- command = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:@"com.apple.installer"];
+ command = [[NSBundle bundleWithIdentifier:@"com.apple.installer"] executablePath];
args = [NSArray arrayWithObjects:path, nil];
} else {
// 10.5 and later. Run installer using the "open" command to ensure it is launched in front of current application.
@@ -60,15 +56,15 @@ + (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegat
if (![[NSFileManager defaultManager] fileExistsAtPath:command])
{
NSError *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingInstallerToolError userInfo:[NSDictionary dictionaryWithObject:@"Couldn't find Apple's installer tool!" forKey:NSLocalizedDescriptionKey]];
- [self _finishInstallationWithResult:NO host:host error:error delegate:delegate];
+ [self finishInstallationWithResult:NO host:host error:error delegate:delegate];
}
else
{
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:command, SUPackageInstallerCommandKey, args, SUPackageInstallerArgumentsKey, host, SUPackageInstallerHostKey, delegate, SUPackageInstallerDelegateKey, nil];
if (synchronously)
- [self _performInstallationWithInfo:info];
+ [self performInstallationWithInfo:info];
else
- [NSThread detachNewThreadSelector:@selector(_performInstallationWithInfo:) toTarget:self withObject:info];
+ [NSThread detachNewThreadSelector:@selector(performInstallationWithInfo:) toTarget:self withObject:info];
}
}