Permalink
Browse files

Merge current TOT from upstream

  • Loading branch information...
2 parents ced3a44 + 16cdd6b commit 08be9202adcc94f61a8d458a7472c92b70fce1ee @ryannielsen ryannielsen committed Oct 31, 2012
Showing with 9,618 additions and 2,261 deletions.
  1. +1 −1 Configurations/ConfigBinaryDeltaDebug.xcconfig
  2. +1 −0 Configurations/ConfigCommon.xcconfig
  3. +1 −1 Configurations/ConfigFrameworkDebug.xcconfig
  4. +1 −1 Configurations/ConfigRelaunchDebug.xcconfig
  5. +1 −1 Configurations/ConfigTestAppDebug.xcconfig
  6. +6 −0 NTSynchronousTask.h
  7. +1 −0 NTSynchronousTask.m
  8. +9 −1 SUAppcast.m
  9. +2 −0 SUAppcastItem.h
  10. +12 −0 SUAppcastItem.m
  11. +15 −2 SUBasicUpdateDriver.m
  12. +86 −15 SUDiskImageUnarchiver.m
  13. +1 −1 SUHost.m
  14. +2 −2 SUInstaller.h
  15. +7 −7 SUInstaller.m
  16. +5 −4 SUPackageInstaller.m
  17. +26 −0 SUPasswordPrompt.h
  18. +86 −0 SUPasswordPrompt.m
  19. +5 −0 SUPipedUnarchiver.m
  20. +1 −1 SUPlainInstaller.h
  21. +7 −5 SUPlainInstaller.m
  22. +14 −0 SUUIBasedUpdateDriver.m
  23. +1 −0 SUUnarchiver.h
  24. +4 −2 SUUpdateAlert.m
  25. +2 −0 SUUpdateDriver.h
  26. +11 −0 SUUpdateDriver.m
  27. +63 −0 Sparkle.xcodeproj/project.pbxproj
  28. +616 −0 ar.lproj/SUAutomaticUpdateAlert.xib
  29. +603 −0 ar.lproj/SUPasswordPrompt.xib
  30. +1,045 −0 ar.lproj/SUUpdateAlert.xib
  31. +1,176 −0 ar.lproj/SUUpdatePermissionPrompt.xib
  32. BIN ar.lproj/Sparkle.strings
  33. BIN da.lproj/SUAutomaticUpdateAlert.strings
  34. +52 −73 da.lproj/SUAutomaticUpdateAlert.xib
  35. BIN da.lproj/SUStatus.strings
  36. BIN da.lproj/SUUpdateAlert.strings
  37. +229 −1,070 da.lproj/SUUpdateAlert.xib
  38. BIN da.lproj/SUUpdatePermissionPrompt.strings
  39. 0 da.lproj/SUUpdatePermissionPrompt.xib
  40. BIN da.lproj/Sparkle.strings
  41. +606 −0 en.lproj/SUPasswordPrompt.xib
  42. +5 −1 finish_installation.m
  43. +606 −0 pt-BR.lproj/SUPasswordPrompt.xib
  44. BIN pt_BR.lproj/SUAutomaticUpdateAlert.strings
  45. +63 −80 pt_BR.lproj/SUAutomaticUpdateAlert.xib
  46. +597 −0 pt_BR.lproj/SUPasswordPrompt.xib
  47. BIN pt_BR.lproj/SUStatus.strings
  48. BIN pt_BR.lproj/SUUpdateAlert.strings
  49. +245 −847 pt_BR.lproj/SUUpdateAlert.xib
  50. 0 pt_BR.lproj/SUUpdatePermissionPrompt.strings
  51. +104 −146 pt_BR.lproj/SUUpdatePermissionPrompt.xib
  52. BIN pt_BR.lproj/Sparkle.strings
  53. +674 −0 th.lproj/SUAutomaticUpdateAlert.xib
  54. +568 −0 th.lproj/SUPasswordPrompt.xib
  55. +898 −0 th.lproj/SUUpdateAlert.xib
  56. +1,160 −0 th.lproj/SUUpdatePermissionPrompt.xib
  57. BIN th.lproj/Sparkle.strings
@@ -2,4 +2,4 @@
#include "ConfigCommonDebug.xcconfig"
#include "ConfigBinaryDelta.xcconfig"
-OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
+OTHER_CFLAGS = -DDEBUG
@@ -17,6 +17,7 @@ GCC_FAST_OBJC_DISPATCH = YES
GCC_ENABLE_PASCAL_STRINGS = NO
ARCHS = ppc i386 x86_64
GCC_VERSION = com.apple.compilers.llvm.clang.1_0
+CLANG_LINK_OBJC_RUNTIME = NO
// Enable warnings
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
@@ -2,4 +2,4 @@
#include "ConfigCommonDebug.xcconfig"
#include "ConfigFramework.xcconfig"
-OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
+OTHER_CFLAGS = -DDEBUG
@@ -2,4 +2,4 @@
#include "ConfigCommonDebug.xcconfig"
#include "ConfigRelaunch.xcconfig"
-OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
+OTHER_CFLAGS = -DDEBUG
@@ -2,4 +2,4 @@
#include "ConfigCommonDebug.xcconfig"
#include "ConfigTestApp.xcconfig"
-OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
+OTHER_CFLAGS = -DDEBUG
View
@@ -25,6 +25,12 @@
// returns the result
+(int) task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input output: (NSData**)outData;
++(NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
+
+- (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input;
+- (int)result;
+- (NSData *)output;
+
@end
#endif
View
@@ -128,6 +128,7 @@ - (id)init;
[[self task] setStandardInput:[self inputPipe]];
[[self task] setStandardOutput:[self outputPipe]];
+ [[self task] setStandardError:[self outputPipe]];
}
return self;
View
@@ -94,7 +94,15 @@ - (void)downloadDidFinish:(NSURLDownload *)aDownload
if (downloadFilename)
{
- document = [[[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:downloadFilename] options:0 error:&error] autorelease];
+ NSUInteger options = 0;
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7) {
+ // In order to avoid including external entities when parsing the appcast (a potential security vulnerability; see https://github.com/andymatuschak/Sparkle/issues/169), we ask NSXMLDocument to "tidy" the XML first. This happens to remove these external entities; it wouldn't be a future-proof approach, but it worked in these historical versions of OS X, and we have a more rigorous approach for 10.7+.
+ options = NSXMLDocumentTidyXML;
+ } else {
+ // In 10.7 and later, there's a real option for the behavior we desire.
+ options = NSXMLNodeLoadExternalEntitiesSameOriginOnly;
+ }
+ document = [[[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:downloadFilename] options:options error:&error] autorelease];
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
[[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil];
View
@@ -20,6 +20,7 @@
NSString *DSASignature;
NSString *minimumSystemVersion;
+ NSString *maximumSystemVersion;
NSURL *fileURL;
NSString *versionString;
@@ -45,6 +46,7 @@
- (NSURL *)fileURL;
- (NSString *)DSASignature;
- (NSString *)minimumSystemVersion;
+- (NSString *)maximumSystemVersion;
- (NSDictionary *)deltaUpdates;
- (BOOL)isDeltaUpdate;
View
@@ -106,6 +106,14 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString
minimumSystemVersion = [systemVersionString copy];
}
+- (NSString *)maximumSystemVersion { return [[maximumSystemVersion retain] autorelease]; }
+- (void)setMaximumSystemVersion:(NSString *)systemVersionString
+{
+ if (maximumSystemVersion == systemVersionString) return;
+ [maximumSystemVersion release];
+ maximumSystemVersion = [systemVersionString copy];
+}
+
- (NSURL *)infoURL { return [[infoURL retain] autorelease]; } // UK 2007-08-31 (whole method)
@@ -210,8 +218,12 @@ - (BOOL)isDeltaUpdate
[self setVersionString: newVersion];
[self setMinimumSystemVersion: [dict objectForKey:@"sparkle:minimumSystemVersion"]];
+ [self setMaximumSystemVersion: [dict objectForKey:@"sparkle:maximumSystemVersion"]];
NSString *shortVersionString = [enclosure objectForKey:@"sparkle:shortVersionString"];
+ if (nil == shortVersionString)
+ shortVersionString = [dict objectForKey:@"sparkle:shortVersionString"]; // fall back on the <item>
+
if (shortVersionString)
[self setDisplayVersionString: shortVersionString];
else
View
@@ -66,8 +66,21 @@ - (BOOL)isItemNewer:(SUAppcastItem *)ui
- (BOOL)hostSupportsItem:(SUAppcastItem *)ui
{
- if ([ui minimumSystemVersion] == nil || [[ui minimumSystemVersion] isEqualToString:@""]) { return YES; }
- return [[SUStandardVersionComparator defaultComparator] compareVersion:[ui minimumSystemVersion] toVersion:[SUHost systemVersionString]] != NSOrderedDescending;
+ if (([ui minimumSystemVersion] == nil || [[ui minimumSystemVersion] isEqualToString:@""]) &&
+ ([ui maximumSystemVersion] == nil || [[ui maximumSystemVersion] isEqualToString:@""])) { return YES; }
+
+ BOOL minimumVersionOK = TRUE;
+ BOOL maximumVersionOK = TRUE;
+
+ // Check minimum and maximum System Version
+ if ([ui minimumSystemVersion] != nil && ![[ui minimumSystemVersion] isEqualToString:@""]) {
+ minimumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui minimumSystemVersion] toVersion:[SUHost systemVersionString]] != NSOrderedDescending;
+ }
+ if ([ui maximumSystemVersion] != nil && ![[ui maximumSystemVersion] isEqualToString:@""]) {
+ maximumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui maximumSystemVersion] toVersion:[SUHost systemVersionString]] != NSOrderedAscending;
+ }
+
+ return minimumVersionOK && maximumVersionOK;
}
- (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui
View
@@ -12,19 +12,35 @@
#import "SULog.h"
#import <CoreServices/CoreServices.h>
-
@implementation SUDiskImageUnarchiver
+ (BOOL)canUnarchivePath:(NSString *)path
{
return [[path pathExtension] isEqualToString:@"dmg"];
}
+// Called on a non-main thread.
- (void)extractDMG
-{
- // GETS CALLED ON NON-MAIN THREAD!!!
+{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSData *result = [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:[NSArray arrayWithObjects: @"isencrypted", archivePath, nil] input:NULL];
+ if([self isEncrypted:result] && [delegate respondsToSelector:@selector(unarchiver:requiresPasswordReturnedViaInvocation:)]) {
+ [self performSelectorOnMainThread:@selector(requestPasswordFromDelegate) withObject:nil waitUntilDone:NO];
+ } else {
+ [self extractDMGWithPassword:nil];
+ }
+
+ [pool release];
+}
+
+// Called on a non-main thread.
+- (void)extractDMGWithPassword:(NSString *)password
+{
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
BOOL mountedSuccessfully = NO;
SULog(@"Extracting %@ as a DMG", archivePath);
@@ -48,25 +64,52 @@ - (void)extractDMG
}
}
while (noErr == FSPathMakeRefWithOptions((UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL));
+
+ NSData *promptData = nil;
+ if (password) {
+ NSString *data = [NSString stringWithFormat:@"%@\nyes\n", password];
+ const char *bytes = [data cStringUsingEncoding:NSUTF8StringEncoding];
+ NSUInteger length = [data lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ promptData = [NSData dataWithBytes:bytes length:length];
+ }
+ else
+ promptData = [NSData dataWithBytes:"yes\n" length:4];
- NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, /*@"-noverify",*/ @"-nobrowse", @"-noautoopen", nil];
- // set up a pipe and push "yes" (y works too), this will accept any license agreement crap
- // not every .dmg needs this, but this will make sure it works with everyone
- NSData* yesData = [[[NSData alloc] initWithBytes:"yes\n" length:4] autorelease];
+ NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, /*@"-noverify",*/ @"-nobrowse", @"-noautoopen", nil];
+
+ NSData *output = nil;
+ NSInteger taskResult = -1;
+ @try
+ {
+ NTSynchronousTask* task = [[NTSynchronousTask alloc] init];
+
+ [task run:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:promptData];
+
+ taskResult = [task result];
+ output = [[[task output] copy] autorelease];
+ [task release];
+ }
+ @catch (NSException *localException)
+ {
+ goto reportError;
+ }
- NSData *output = nil;
- int returnCode = [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData output: &output];
- if ( returnCode != 0 )
+ if (taskResult != 0)
{
NSString* resultStr = output ? [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease] : nil;
- SULog( @"hdiutil failed with code: %d data: <<%@>>", returnCode, resultStr );
- goto reportError;
+ if (password != nil && [resultStr rangeOfString:@"Authentication error"].location != NSNotFound && [delegate respondsToSelector:@selector(unarchiver:requiresPasswordReturnedViaInvocation:)]) {
+ [self performSelectorOnMainThread:@selector(requestPasswordFromDelegate) withObject:nil waitUntilDone:NO];
+ goto finally;
+ } else {
+ SULog( @"hdiutil failed with code: %d data: <<%@>>", taskResult, resultStr );
+ goto reportError;
+ }
}
mountedSuccessfully = YES;
// Now that we've mounted it, we need to copy out its contents.
- if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
- // On 10.6 and later we don't want to use the File Manager API and instead want to use NSFileManager (fixes #827357).
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) {
+ // On 10.7 and later we don't want to use the File Manager API and instead want to use NSFileManager (fixes #827357).
NSFileManager *manager = [[[NSFileManager alloc] init] autorelease];
NSError *error = nil;
NSArray *contents = [manager contentsOfDirectoryAtPath:mountPoint error:&error];
@@ -91,7 +134,7 @@ - (void)extractDMG
if (![manager copyItemAtPath:fromPath toPath:toPath error:&error])
{
- SULog(@"Couldn't copy item: %@", error);
+ SULog(@"Couldn't copy item: %@ : %@", error, error.userInfo ? error.userInfo : @"");
goto reportError;
}
}
@@ -132,4 +175,32 @@ + (void)load
[self registerImplementation:self];
}
+- (BOOL)isEncrypted:(NSData*)resultData
+{
+ BOOL result = NO;
+ if(resultData)
+ {
+ NSString *data = [NSString stringWithCString:(char*)[resultData bytes] encoding:NSUTF8StringEncoding];
+ if (!NSEqualRanges([data rangeOfString:@"passphrase-count"], NSMakeRange(NSNotFound, 0)))
+ {
+ result = YES;
+ }
+ }
+ return result;
+}
+
+- (void)requestPasswordFromDelegate
+{
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(continueWithPassword:)]];
+ [invocation setSelector:@selector(continueWithPassword:)];
+ [invocation setTarget:self];
+ [invocation retainArguments];
+ [delegate unarchiver:self requiresPasswordReturnedViaInvocation:invocation];
+}
+
+- (void)continueWithPassword:(NSString *)password
+{
+ [NSThread detachNewThreadSelector:@selector(extractDMGWithPassword:) toTarget:self withObject:password];
+}
+
@end
View
@@ -256,7 +256,7 @@ + (NSString *)systemVersionString
OSErr err3 = Gestalt(gestaltSystemVersionBugFix, &bugfix);
if (!err1 && !err2 && !err3)
{
- verStr = [NSString stringWithFormat:@"%d.%d.%d", major, minor, bugfix];
+ verStr = [NSString stringWithFormat:@"%ld.%ld.%ld", (long)major, (long)minor, (long)bugfix];
}
else
#endif
View
@@ -14,8 +14,8 @@
@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;
++ (void) installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator;
++ (void) finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate;
+ (NSString*) updateFolder;
+ (void) notifyDelegateOfFailure: (NSDictionary*)dict;
@end
View
@@ -43,7 +43,7 @@ + (BOOL)isAliasFolderAtPath:(NSString *)path
}
-+ (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
++ (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
{
// Search subdirectories for the application
NSString *currentFile,
@@ -109,23 +109,23 @@ + (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 finishInstallationToPath:installationPath withResult: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
{
- [(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationWithPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously versionComparator:comparator];
+ [(isPackage ? [SUPackageInstaller class] : [SUPlainInstaller class]) performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host delegate:delegate synchronously:synchronously versionComparator:comparator];
}
}
-+ (void)mdimportHost:(SUHost *)host
++ (void)mdimportInstallationPath:(NSString *)installationPath
{
// *** GETS CALLED ON NON-MAIN THREAD!
SULog( @"mdimporting" );
NSTask *mdimport = [[[NSTask alloc] init] autorelease];
[mdimport setLaunchPath:@"/usr/bin/mdimport"];
- [mdimport setArguments:[NSArray arrayWithObject:[host installationPath]]];
+ [mdimport setArguments:[NSArray arrayWithObject:installationPath]];
@try
{
[mdimport launch];
@@ -143,11 +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)finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate
{
if (result)
{
- [self mdimportHost:host];
+ [self mdimportInstallationPath:installationPath];
if ([delegate respondsToSelector:@selector(installerFinishedForHost:)])
[delegate performSelectorOnMainThread: @selector(installerFinishedForHost:) withObject: host waitUntilDone: NO];
}
View
@@ -14,12 +14,13 @@
NSString *SUPackageInstallerArgumentsKey = @"SUPackageInstallerArguments";
NSString *SUPackageInstallerHostKey = @"SUPackageInstallerHost";
NSString *SUPackageInstallerDelegateKey = @"SUPackageInstallerDelegate";
+NSString *SUPackageInstallerInstallationPathKey = @"SUPackageInstallerInstallationPathKey";
@implementation SUPackageInstaller
+ (void)finishInstallationWithInfo:(NSDictionary *)info
{
- [self finishInstallationWithResult:YES host:[info objectForKey:SUPackageInstallerHostKey] error:nil delegate:[info objectForKey:SUPackageInstallerDelegateKey]];
+ [self finishInstallationToPath:[info objectForKey:SUPackageInstallerInstallationPathKey] withResult:YES host:[info objectForKey:SUPackageInstallerHostKey] error:nil delegate:[info objectForKey:SUPackageInstallerDelegateKey]];
}
+ (void)performInstallationWithInfo:(NSDictionary *)info
@@ -35,7 +36,7 @@ + (void)performInstallationWithInfo:(NSDictionary *)info
[pool drain];
}
-+ (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
++ (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id <SUVersionComparison>)comparator
{
NSString *command;
NSArray *args;
@@ -56,11 +57,11 @@ + (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 finishInstallationToPath:installationPath withResult:NO host:host error:error delegate:delegate];
}
else
{
- NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:command, SUPackageInstallerCommandKey, args, SUPackageInstallerArgumentsKey, host, SUPackageInstallerHostKey, delegate, SUPackageInstallerDelegateKey, nil];
+ NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:command, SUPackageInstallerCommandKey, args, SUPackageInstallerArgumentsKey, host, SUPackageInstallerHostKey, delegate, SUPackageInstallerDelegateKey, installationPath, SUPackageInstallerInstallationPathKey, nil];
if (synchronously)
[self performInstallationWithInfo:info];
else
Oops, something went wrong.

0 comments on commit 08be920

Please sign in to comment.