From 43a0a7db4f806985df073c899da783eacab75e73 Mon Sep 17 00:00:00 2001 From: Uli Kusterer Date: Fri, 4 Dec 2009 08:40:46 +0100 Subject: [PATCH] Merge of changes from SVN repository: - Changed NTSynchronousTask to also give the status return value and direct stderror output to the outputData. - Changed includes so this builds as part of an app, too, not just as a framework - Made sure SUAppcast's dealloc releases some leaked ivars. - Added infoURL, extracted from link, that can point to a "more Info" page for download-less URLs - Added support for version attribute on item so we can support update notifications that don't include an enclosure (e.g. paid upgrades, or upgrades that would require a system update) - Added/improved a few description methods to ease debugging. - Added SULog so one can ask for a special log with additional information when there are update issues. - Added mayUpdateAndRestart for apps that absolutely, positively can't restart right now (e.g. cuz they're burning a CD and would produce a coaster). - Added updaterWillRelaunchApplication delegate method, analogous to the notification. Useful to have app delegate quit helper apps during installation. - Made SUBasicUpdateDriver's abortUpdate implicitly retain/autorelease the update driver, because the notification center otherwise releases it and it goes away, causing crashes in superclass's abortUpdate. - Merge of SUKeepDownloadOnFailedInstallKey and SUFixedHTMLDisplaySizeKey. - Avoid a few warnings about missing prototypes - Be paranoid, hdiutil can verify the download again, so let it. Better for internal apps where we turn off DSA checks, too. - SUHost has an -installationPath now, independent from the bundlePath, so one can normalize the app name from "MyApp 1.1b4" back to "MyApp" Users assume the file name contains the correct version number when there is one in it. Saves support a few round-trips each time. - Be better at threading: Try calling non-thread-safe methods on main thread only, and don't assume delegates know when they need to be thread-safe, call them on main thread where possible. - Added a method to put the old copy of the app in the trash. 1.5git changed in this spot, so I didn't actually merge the code that uses it back in yet. - Fix version comparison so it doesn't get confused by bracketed build numbers in version strings - Make sure cancel button is disabled during extraction, otherwise user would crash. - Don't put auto-update window at floating window level. It's huge and can't be switched to background! If you're an NSBGOnly where you need that, turn it on only in that case, but don't generally do such nonsense. - Hide release notes view if there aren't any. - Test whether we are on dial-up before checking for updates in background. It's not nice to cause (possibly expensive) dial-up periodically. - Temporarily comment out DSA complaints for easier testing. - Don't store (possibly already invalidated) one-shot NSTimers in an ivar. It's bad style. Retain it instead. - Decompress some monster expressions with nested method calls in ternary operators and nested in method calls again. - Don't use implicit "id" for params or return types. - finish_installation now puts up a progress window, so user knows update is still not finished. - Use ThreadSafePreferences (included dummy version that uses regular prefs for projects that don't use ThreadSafePreferences). - Todo later: Change finish_installation to be prettier. --- Elgato/ThreadSafePreferences.h | 15 ++ NTSynchronousTask.h | 2 +- NTSynchronousTask.m | 26 +- SUAppcast.m | 20 +- SUAppcastItem.h | 4 + SUAppcastItem.m | 32 ++- SUAutomaticUpdateAlert.m | 2 +- SUAutomaticUpdateDriver.m | 1 + SUBasicUpdateDriver.m | 80 +++--- SUConstants.h | 20 ++ SUConstants.m | 8 +- SUDSAVerifier.h | 2 + SUDSAVerifier.m | 6 + SUDiskImageUnarchiver.m | 20 +- SUHost.h | 6 +- SUHost.m | 28 +- SUInstaller.h | 2 + SUInstaller.m | 46 +++- SULog.h | 31 +++ SULog.m | 80 ++++++ SUPackageInstaller.h | 6 +- SUPackageInstaller.m | 1 + SUPipedUnarchiver.m | 16 +- SUPlainInstaller.h | 7 +- SUPlainInstaller.m | 6 + SUPlainInstallerInternals.m | 136 +++++++--- SUScheduledUpdateDriver.m | 6 +- SUStandardVersionComparator.m | 16 +- SUStatus.nib/classes.nib | 56 ++++ SUStatus.nib/info.nib | 20 ++ SUStatusController.m | 15 +- SUSystemProfiler.h | 2 + SUUIBasedUpdateDriver.h | 4 +- SUUIBasedUpdateDriver.m | 18 +- SUUnarchiver.m | 6 +- SUUpdateAlert.h | 5 +- SUUpdateAlert.m | 88 ++++++- SUUpdateDriver.m | 3 +- SUUpdatePermissionPrompt.m | 1 + SUUpdater.h | 26 +- SUUpdater.m | 108 +++++++- SUVersionComparisonProtocol.h | 4 +- Sparkle.xcodeproj/default.pbxuser | 245 ------------------ Sparkle.xcodeproj/project.pbxproj | 19 ++ .../English.lproj/MainMenu.nib/classes.nib | 43 +++ .../English.lproj/MainMenu.nib/info.nib | 21 ++ .../SUAutomaticUpdateAlert.nib/classes.nib | 29 +++ da.lproj/SUAutomaticUpdateAlert.nib/info.nib | 18 ++ da.lproj/SUAutomaticUpdateAlert.strings | Bin da.lproj/SUUpdateAlert.nib/classes.nib | 40 +++ da.lproj/SUUpdateAlert.nib/info.nib | 22 ++ da.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 11557 -> 10365 bytes da.lproj/SUUpdateAlert.strings | Bin .../SUUpdatePermissionPrompt.nib/classes.nib | 34 +++ .../data.dependency | 10 + .../SUUpdatePermissionPrompt.nib/info.nib | 16 ++ .../keyedobjects.nib | Bin 15232 -> 12807 bytes da.lproj/Sparkle.strings | Bin de.lproj/SUAutomaticUpdateAlert.strings | Bin 1074 -> 1326 bytes de.lproj/SUUpdateAlert.strings | Bin 1682 -> 1682 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ de.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3058 bytes de.lproj/Sparkle.strings | Bin 8884 -> 8884 bytes en.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3038 bytes es.lproj/SUAutomaticUpdateAlert.strings | Bin 0 -> 1262 bytes es.lproj/SUUpdateAlert.strings | Bin 0 -> 1668 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ es.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3172 bytes finish_installation.m | 26 +- .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ fr.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3144 bytes it.lproj/SUAutomaticUpdateAlert.strings | Bin 0 -> 1258 bytes it.lproj/SUUpdateAlert.strings | Bin 0 -> 1658 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ it.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3120 bytes ja.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 2750 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 16 ++ .../SUAutomaticUpdateAlert.nib/classes.nib | 50 ++++ pt.lproj/SUAutomaticUpdateAlert.nib/info.nib | 18 ++ .../keyedobjects.nib | Bin 0 -> 7272 bytes pt.lproj/SUAutomaticUpdateAlert.strings | 12 + pt.lproj/SUUpdateAlert.nib/classes.nib | 69 +++++ pt.lproj/SUUpdateAlert.nib/info.nib | 18 ++ pt.lproj/SUUpdateAlert.nib/keyedobjects.nib | Bin 0 -> 10678 bytes pt.lproj/SUUpdateAlert.strings | Bin 0 -> 1676 bytes .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ .../keyedobjects.nib | Bin 0 -> 12573 bytes pt.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 3036 bytes pt.lproj/Sparkle.strings | 95 +++++++ .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 18 ++ .../SUUpdatePermissionPrompt.nib/classes.nib | 59 +++++ .../SUUpdatePermissionPrompt.nib/info.nib | 20 ++ zh_CN.lproj/SUUpdatePermissionPrompt.strings | Bin 0 -> 2628 bytes 100 files changed, 1985 insertions(+), 397 deletions(-) create mode 100755 Elgato/ThreadSafePreferences.h create mode 100644 SULog.h create mode 100644 SULog.m create mode 100644 SUStatus.nib/classes.nib create mode 100644 SUStatus.nib/info.nib delete mode 100644 Sparkle.xcodeproj/default.pbxuser create mode 100644 Test Application/English.lproj/MainMenu.nib/classes.nib create mode 100644 Test Application/English.lproj/MainMenu.nib/info.nib create mode 100644 da.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 da.lproj/SUAutomaticUpdateAlert.nib/info.nib mode change 100755 => 100644 da.lproj/SUAutomaticUpdateAlert.strings create mode 100644 da.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 da.lproj/SUUpdateAlert.nib/info.nib mode change 100755 => 100644 da.lproj/SUUpdateAlert.strings create mode 100644 da.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 da.lproj/SUUpdatePermissionPrompt.nib/data.dependency create mode 100644 da.lproj/SUUpdatePermissionPrompt.nib/info.nib mode change 100755 => 100644 da.lproj/Sparkle.strings create mode 100644 de.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 de.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 de.lproj/SUUpdatePermissionPrompt.strings create mode 100644 en.lproj/SUUpdatePermissionPrompt.strings create mode 100644 es.lproj/SUAutomaticUpdateAlert.strings create mode 100644 es.lproj/SUUpdateAlert.strings create mode 100644 es.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 es.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 es.lproj/SUUpdatePermissionPrompt.strings create mode 100644 fr.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 fr.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 fr.lproj/SUUpdatePermissionPrompt.strings create mode 100644 it.lproj/SUAutomaticUpdateAlert.strings create mode 100644 it.lproj/SUUpdateAlert.strings create mode 100644 it.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 it.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 it.lproj/SUUpdatePermissionPrompt.strings create mode 100644 ja.lproj/SUUpdatePermissionPrompt.strings create mode 100644 nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 nl.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 pt.lproj/SUAutomaticUpdateAlert.nib/classes.nib create mode 100644 pt.lproj/SUAutomaticUpdateAlert.nib/info.nib create mode 100644 pt.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib create mode 100644 pt.lproj/SUAutomaticUpdateAlert.strings create mode 100644 pt.lproj/SUUpdateAlert.nib/classes.nib create mode 100644 pt.lproj/SUUpdateAlert.nib/info.nib create mode 100644 pt.lproj/SUUpdateAlert.nib/keyedobjects.nib create mode 100644 pt.lproj/SUUpdateAlert.strings create mode 100644 pt.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 pt.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 pt.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib create mode 100644 pt.lproj/SUUpdatePermissionPrompt.strings create mode 100644 pt.lproj/Sparkle.strings create mode 100644 ru.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 ru.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib create mode 100644 sv.lproj/SUUpdatePermissionPrompt.nib/info.nib create mode 100644 zh_CN.lproj/SUUpdatePermissionPrompt.strings diff --git a/Elgato/ThreadSafePreferences.h b/Elgato/ThreadSafePreferences.h new file mode 100755 index 000000000..4545cb02f --- /dev/null +++ b/Elgato/ThreadSafePreferences.h @@ -0,0 +1,15 @@ +// Header that shouldn't be included in anything but the Sparkle finish_installation tool +// because it includes files also used in EyeTV, but doesn't need to be thread safe +// as it's a single-threaded process anyway. + +#if !EYETV && !__TOAST__ && !TURBO + +#define ThreadSafePreferences_CopyAppValue CFPreferencesCopyAppValue +#define ThreadSafePreferences_SetValue CFPreferencesSetValue +#define ThreadSafePreferences_Synchronize CFPreferencesSynchronize + +#else + +#error This header shouldn't be included here! + +#endif diff --git a/NTSynchronousTask.h b/NTSynchronousTask.h index 1cf5c04e2..7f2a14d16 100644 --- a/NTSynchronousTask.h +++ b/NTSynchronousTask.h @@ -22,7 +22,7 @@ // pass nil for directory if not needed // returns the result -+ (NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input; ++(int) task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input output: (NSData**)outData; @end diff --git a/NTSynchronousTask.m b/NTSynchronousTask.m index d506cad5b..b64c2dab8 100644 --- a/NTSynchronousTask.m +++ b/NTSynchronousTask.m @@ -6,7 +6,11 @@ // Copyright 2005 Steve Gehrman. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "NTSynchronousTask.h" @interface NTSynchronousTask (Private) @@ -44,6 +48,7 @@ - (id)init; [[self task] setStandardInput:[self inputPipe]]; [[self task] setStandardOutput:[self outputPipe]]; + [[self task] setStandardError:[self outputPipe]]; } return self; @@ -64,11 +69,13 @@ - (void)dealloc [super dealloc]; } -+ (NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input; ++(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]; - NSData* result=nil; + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int taskResult = 0; + if( outData ) + *outData = nil; NS_DURING { @@ -76,20 +83,23 @@ + (NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withAr [task run:toolPath directory:currentDirectory withArgs:args input:input]; - if ([task result] == 0) - result = [[task output] retain]; + taskResult = [task result]; + if( outData ) + *outData = [[task output] retain]; [task release]; } NS_HANDLER; + taskResult = errCppGeneral; NS_ENDHANDLER; [pool drain]; // retained above - [result autorelease]; + if( outData ) + [*outData autorelease]; - return result; + return taskResult; } @end diff --git a/SUAppcast.m b/SUAppcast.m index 056b2d815..41ef2dffb 100644 --- a/SUAppcast.m +++ b/SUAppcast.m @@ -6,8 +6,15 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUAppcast.h" +#import "SUConstants.h" +#import "SULog.h" + @interface SUAppcast (Private) - (void)reportError:(NSError *)error; @@ -19,7 +26,12 @@ @implementation SUAppcast - (void)dealloc { [items release]; + items = nil; [userAgentString release]; + userAgentString = nil; + [downloadFilename release]; + downloadFilename = nil; + [super dealloc]; } @@ -59,6 +71,12 @@ - (void)downloadDidFinish:(NSURLDownload *)download 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 MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 [[NSFileManager defaultManager] removeFileAtPath:downloadFilename handler:nil]; #else diff --git a/SUAppcastItem.h b/SUAppcastItem.h index 010c892ae..6cbd4748d 100644 --- a/SUAppcastItem.h +++ b/SUAppcastItem.h @@ -24,6 +24,8 @@ NSString *displayVersionString; NSDictionary *propertiesDictionary; + + NSURL *infoURL; // UK 2007-08-31 } // Initializes with data from a dictionary provided by the RSS class. @@ -43,6 +45,8 @@ // Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. - (NSDictionary *)propertiesDictionary; +- (NSURL *)infoURL; // UK 2007-08-31 + @end #endif diff --git a/SUAppcastItem.m b/SUAppcastItem.m index f17ce4935..116ab1571 100644 --- a/SUAppcastItem.m +++ b/SUAppcastItem.m @@ -6,7 +6,11 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUAppcastItem.h" @implementation SUAppcastItem @@ -101,6 +105,16 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString minimumSystemVersion = [systemVersionString copy]; } + +- (NSURL *)infoURL { return [[infoURL retain] autorelease]; } // UK 2007-08-31 (whole method) + +- (void)setInfoURL:(NSURL *)aFileURL // UK 2007-08-31 (whole method) +{ + if( aFileURL == infoURL ) return; + [infoURL release]; + infoURL = [aFileURL copy]; +} + - initWithDictionary:(NSDictionary *)dict { return [self initWithDictionary:dict failureReason:nil]; @@ -130,7 +144,9 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString // The big caveat with this is that you can't have underscores in your version strings, as that'll confuse Sparkle. // Feel free to change the separator string to a hyphen or something more suited to your needs if you like. NSString *newVersion = [enclosure objectForKey:@"sparkle:version"]; - if (newVersion == nil) // no sparkle:version attribute + if( newVersion == nil ) + newVersion = [dict objectForKey:@"sparkle:version"]; // UK 2007-08-31 Get version from the item, in case it's a download-less item (i.e. paid upgrade). + if (newVersion == nil) // no sparkle:version attribute anywhere? { // Separate the url by underscores and take the last component, as that'll be closest to the end, // then we remove the extension. Hopefully, this will be the version. @@ -160,7 +176,16 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString [self setDate:[dict objectForKey:@"pubDate"]]; [self setItemDescription:[dict objectForKey:@"description"]]; - [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; + NSString* theInfoURL = [dict objectForKey:@"link"]; + if( theInfoURL ) + { + if( ![theInfoURL isKindOfClass: [NSString class]] ) + NSLog(@"SUAppcastItem -initWithDictionary: Info URL is not of valid type."); + else + [self setInfoURL:[NSURL URLWithString:theInfoURL]]; + } + + [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; [self setDSASignature:[enclosure objectForKey:@"sparkle:dsaSignature"]]; [self setVersionString:newVersion]; @@ -193,6 +218,7 @@ - (void)dealloc [self setFileURL:nil]; [self setVersionString:nil]; [self setDisplayVersionString:nil]; + [self setInfoURL:nil]; [propertiesDictionary release]; [super dealloc]; } diff --git a/SUAutomaticUpdateAlert.m b/SUAutomaticUpdateAlert.m index d30025f11..f055f48d1 100644 --- a/SUAutomaticUpdateAlert.m +++ b/SUAutomaticUpdateAlert.m @@ -33,7 +33,7 @@ - (void)dealloc [super dealloc]; } -- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; } +- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [host bundlePath], [host installationPath]]; } - (IBAction)installNow:sender { diff --git a/SUAutomaticUpdateDriver.m b/SUAutomaticUpdateDriver.m index 9a4098211..fb8fcd1ad 100644 --- a/SUAutomaticUpdateDriver.m +++ b/SUAutomaticUpdateDriver.m @@ -10,6 +10,7 @@ #import "SUAutomaticUpdateAlert.h" #import "SUHost.h" +#import "SUConstants.h" @implementation SUAutomaticUpdateDriver diff --git a/SUBasicUpdateDriver.m b/SUBasicUpdateDriver.m index 2e2025d8c..dba63e16b 100644 --- a/SUBasicUpdateDriver.m +++ b/SUBasicUpdateDriver.m @@ -13,6 +13,9 @@ #import "SUInstaller.h" #import "SUStandardVersionComparator.h" #import "SUUnarchiver.h" +#import "SUConstants.h" +#import "SULog.h" + @implementation SUBasicUpdateDriver @@ -165,6 +168,7 @@ - (void)download:(NSURLDownload *)d decideDestinationWithSuggestedFilename:(NSSt - (void)downloadDidFinish:(NSURLDownload *)d { + #if 0 // +++ // New in Sparkle 1.5: we're now checking signatures on all non-secure downloads, where "secure" is defined as both the appcast and the download being transmitted over SSL. NSURL *downloadURL = [[d request] URL]; if (!(([[downloadURL scheme] isEqualToString:@"https"] && [[appcastURL scheme] isEqualToString:@"https"]) || @@ -176,6 +180,7 @@ - (void)downloadDidFinish:(NSURLDownload *)d return; } } + #endif [self extractUpdate]; } @@ -200,7 +205,7 @@ - (void)extractUpdate SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath]; if (!unarchiver) { - NSLog(@"Sparkle Error: No valid unarchiver for %@!", downloadPath); + SULog(@"Sparkle Error: No valid unarchiver for %@!", downloadPath); [self unarchiverDidFail:nil]; return; } @@ -231,7 +236,11 @@ - (void)installUpdate NSString *relaunchPathToCopy = [[NSBundle bundleForClass:[self class]] pathForResource:@"finish_installation" ofType:@""]; NSString *appSupportFolder = [[@"~/Library/Application Support/" stringByExpandingTildeInPath] stringByAppendingPathComponent: [host name]]; NSString *targetPath = [appSupportFolder stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]]; - [[NSFileManager defaultManager] createDirectoryAtPath: targetPath withIntermediateDirectories: YES attributes: [NSDictionary dictionary] error: NULL]; +#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 + [[NSFileManager defaultManager] createDirectoryAtPath: [targetPath stringByDeletingLastPathComponent] attributes: [NSDictionary dictionary]]; +#else + [[NSFileManager defaultManager] createDirectoryAtPath: [targetPath stringByDeletingLastPathComponent] withIntermediateDirectories: YES attributes: [NSDictionary dictionary] error: NULL]; +#endif // Only the paranoid survive: if there's already a stray copy of relaunch there, we would have problems. NSError *error = nil; @@ -251,36 +260,43 @@ - (void)installUpdate - (void)installAndRelaunchWithTool { - // Give the host app an opportunity to postpone the relaunch. - static BOOL postponedOnce = NO; - if (!postponedOnce && [[updater delegate] respondsToSelector:@selector(updater:shouldPostponeRelaunchForUpdate:untilInvoking:)]) + BOOL mayRelaunchAtAll = [updater mayUpdateAndRestart]; + + if( mayRelaunchAtAll ) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(relaunchHostApp)]]; - [invocation setSelector:@selector(relaunchHostApp)]; - [invocation setTarget:self]; - postponedOnce = YES; - if ([[updater delegate] updater:updater shouldPostponeRelaunchForUpdate:updateItem untilInvoking:invocation]) - return; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self]; - if ([[updater delegate] respondsToSelector:@selector(updaterWillRelaunchApplication:)]) - [[updater delegate] updaterWillRelaunchApplication:updater]; + // Give the host app an opportunity to postpone the relaunch. + static BOOL postponedOnce = NO; + if (!postponedOnce && [[updater delegate] respondsToSelector:@selector(updater:shouldPostponeRelaunchForUpdate:untilInvoking:)]) + { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(relaunchHostApp)]]; + [invocation setSelector:@selector(relaunchHostApp)]; + [invocation setTarget:self]; + postponedOnce = YES; + if ([[updater delegate] updater:updater shouldPostponeRelaunchForUpdate:updateItem untilInvoking:invocation]) + return; + } - if(!relaunchPath || ![[NSFileManager defaultManager] fileExistsAtPath:relaunchPath]) - { - // Note that we explicitly use the host app's name here, since updating plugin for Mail relaunches Mail, not just the plugin. - [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [host name]], NSLocalizedDescriptionKey, [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", relaunchPath], NSLocalizedFailureReasonErrorKey, nil]]]; - // We intentionally don't abandon the update here so that the host won't initiate another. - return; - } + [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self]; + if ([[updater delegate] respondsToSelector:@selector(updaterWillRelaunchApplication:)]) + [[updater delegate] updaterWillRelaunchApplication:updater]; - NSString *pathToRelaunch = [host bundlePath]; - if ([[updater delegate] respondsToSelector:@selector(pathToRelaunchForUpdater:)]) - pathToRelaunch = [[updater delegate] pathToRelaunchForUpdater:updater]; - [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], [downloadPath stringByDeletingLastPathComponent], nil]]; - - [NSApp terminate:self]; + if(!relaunchPath || ![[NSFileManager defaultManager] fileExistsAtPath:relaunchPath]) + { + // Note that we explicitly use the host app's name here, since updating plugin for Mail relaunches Mail, not just the plugin. + [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [host name]], NSLocalizedDescriptionKey, [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", relaunchPath], NSLocalizedFailureReasonErrorKey, nil]]]; + // We intentionally don't abandon the update here so that the host won't initiate another. + return; + } + + NSString *pathToRelaunch = [host bundlePath]; + if ([[updater delegate] respondsToSelector:@selector(pathToRelaunchForUpdater:)]) + pathToRelaunch = [[updater delegate] pathToRelaunchForUpdater:updater]; + [NSTask launchedTaskWithLaunchPath:relaunchPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], [downloadPath stringByDeletingLastPathComponent], nil]]; + + [NSApp terminate:self]; + } + else + [self abortUpdate]; } - (void)cleanUp @@ -295,11 +311,13 @@ - (void)cleanUp - (void)installerForHost:(SUHost *)aHost failedWithError:(NSError *)error { if (aHost != host) { return; } + [[NSFileManager defaultManager] removeFileAtPath:relaunchPath handler:NULL]; // Clean up the copied relauncher. [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while installing the update. Please try again later.", nil), NSLocalizedDescriptionKey, [error localizedDescription], NSLocalizedFailureReasonErrorKey, nil]]]; } - (void)abortUpdate { + [[self retain] autorelease]; // In case the notification center was the last one holding on to us. [[NSNotificationCenter defaultCenter] removeObserver:self]; [super abortUpdate]; } @@ -307,9 +325,9 @@ - (void)abortUpdate - (void)abortUpdateWithError:(NSError *)error { if ([error code] != SUNoUpdateError) // Let's not bother logging this. - NSLog(@"Sparkle Error: %@", [error localizedDescription]); + SULog(@"Sparkle Error: %@", [error localizedDescription]); if ([error localizedFailureReason]) - NSLog(@"Sparkle Error (continued): %@", [error localizedFailureReason]); + SULog(@"Sparkle Error (continued): %@", [error localizedFailureReason]); if (download) [download cancel]; [self abortUpdate]; diff --git a/SUConstants.h b/SUConstants.h index e9d8e9586..dcdba8de0 100644 --- a/SUConstants.h +++ b/SUConstants.h @@ -11,9 +11,18 @@ #define SUCONSTANTS_H +// ----------------------------------------------------------------------------- +// Notifications: +// ----------------------------------------------------------------------------- + extern NSString *SUUpdaterWillRestartNotification; + extern NSString *SUTechnicalErrorInformationKey; +// ----------------------------------------------------------------------------- +// PList keys:: +// ----------------------------------------------------------------------------- + extern NSString *SUFeedURLKey; extern NSString *SUHasLaunchedBeforeKey; extern NSString *SUShowReleaseNotesKey; @@ -29,6 +38,12 @@ extern NSString *SUEnableAutomaticChecksKeyOld; extern NSString *SUEnableSystemProfilingKey; extern NSString *SUSendProfileInfoKey; extern NSString *SULastProfileSubmitDateKey; +extern NSString *SUFixedHTMLDisplaySizeKey; +extern NSString *SUKeepDownloadOnFailedInstallKey; + +// ----------------------------------------------------------------------------- +// Errors: +// ----------------------------------------------------------------------------- extern NSString *SUSparkleErrorDomain; // Appcast phase errors. @@ -53,6 +68,11 @@ extern OSStatus SURelaunchError; extern OSStatus SUInstallationError; extern OSStatus SUDowngradeError; + +// ----------------------------------------------------------------------------- +// NSInteger fixer-upper: +// ----------------------------------------------------------------------------- + // NSInteger is a type that was added to Leopard. // Here is some glue to ensure that NSInteger will work with pre-10.5 SDKs: #ifndef NSINTEGER_DEFINED diff --git a/SUConstants.m b/SUConstants.m index ae91584f2..941137bac 100644 --- a/SUConstants.m +++ b/SUConstants.m @@ -6,7 +6,11 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUConstants.h" NSString *SUUpdaterWillRestartNotification = @"SUUpdaterWillRestartNotificationName"; @@ -28,6 +32,8 @@ NSString *SUEnableAutomaticChecksKeyOld = @"SUCheckAtStartup"; NSString *SUSendProfileInfoKey = @"SUSendProfileInfo"; NSString *SULastProfileSubmitDateKey = @"SULastProfileSubmissionDate"; +NSString *SUFixedHTMLDisplaySizeKey = @"SUFixedHTMLDisplaySize"; +NSString *SUKeepDownloadOnFailedInstallKey = @"SUKeepDownloadOnFailedInstall"; NSString *SUSparkleErrorDomain = @"SUSparkleErrorDomain"; OSStatus SUAppcastParseError = 1000; diff --git a/SUDSAVerifier.h b/SUDSAVerifier.h index 785e49c28..dd8a6aadb 100644 --- a/SUDSAVerifier.h +++ b/SUDSAVerifier.h @@ -9,6 +9,8 @@ #ifndef SUDSAVERIFIER_H #define SUDSAVERIFIER_H +#import + // For the paranoid folks! @interface SUDSAVerifier : NSObject {} + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString; diff --git a/SUDSAVerifier.m b/SUDSAVerifier.m index e9ee03e58..ebf419db4 100644 --- a/SUDSAVerifier.m +++ b/SUDSAVerifier.m @@ -9,6 +9,7 @@ // DSA stuff adapted from code provided by Allan Odgaard. Thanks, Allan! #import "SUDSAVerifier.h" +#import #import #import @@ -17,6 +18,11 @@ #import #import +// Alex: Avoid prototype warning +long b64decode(unsigned char* str); +EVP_PKEY* load_dsa_key(char *key); + + long b64decode(unsigned char* str) { unsigned char *cur, *start; diff --git a/SUDiskImageUnarchiver.m b/SUDiskImageUnarchiver.m index a7c5f633c..908a77227 100644 --- a/SUDiskImageUnarchiver.m +++ b/SUDiskImageUnarchiver.m @@ -9,8 +9,10 @@ #import "SUDiskImageUnarchiver.h" #import "SUUnarchiver_Private.h" #import "NTSynchronousTask.h" +#import "SULog.h" #import + @implementation SUDiskImageUnarchiver + (BOOL)_canUnarchivePath:(NSString *)path @@ -25,9 +27,13 @@ - (void)start - (void)_extractDMG { + // GETS CALLED ON NON-MAIN THREAD!!! + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; BOOL mountedSuccessfully = NO; + SULog(@"Extracting %@ as a DMG", archivePath); + // get a unique mount point path NSString *mountPointName = nil; NSString *mountPoint = nil; @@ -46,13 +52,19 @@ - (void)_extractDMG } while (noErr == FSPathMakeRefWithOptions((UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL)); - NSArray* arguments = [NSArray arrayWithObjects:@"attach", archivePath, @"-mountpoint", mountPoint, @"-noverify", @"-nobrowse", @"-noautoopen", nil]; + 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]; - NSData *result = [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData]; - if (!result) goto reportError; + NSData *output = nil; + int returnCode = [NTSynchronousTask task:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:yesData output: &output]; + if ( returnCode != 0 ) + { + NSString* resultStr = output ? [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease] : nil; + SULog( @"hdiutil failed with code: %d data: <<%@>>", returnCode, resultStr ); + goto reportError; + } mountedSuccessfully = YES; // Now that we've mounted it, we need to copy out its contents. @@ -75,6 +87,8 @@ - (void)_extractDMG finally: if (mountedSuccessfully) [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:[NSArray arrayWithObjects:@"detach", mountPoint, @"-force", nil]]; + else + SULog(@"Can't mount DMG %@",archivePath); [pool drain]; } diff --git a/SUHost.h b/SUHost.h index f8f8203fc..136ece08c 100644 --- a/SUHost.h +++ b/SUHost.h @@ -5,7 +5,10 @@ // Copyright 2008 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" @interface SUHost : NSObject { @@ -17,6 +20,7 @@ - (id)initWithBundle:(NSBundle *)aBundle; - (NSBundle *)bundle; - (NSString *)bundlePath; +- (NSString *)installationPath; - (NSString *)name; - (NSString *)version; - (NSString *)displayVersion; diff --git a/SUHost.m b/SUHost.m index 8665438c4..3e4f65db7 100644 --- a/SUHost.m +++ b/SUHost.m @@ -10,6 +10,9 @@ #import "SUConstants.h" #import "SUSystemProfiler.h" #import // For statfs for isRunningOnReadOnlyVolume +#import "ThreadSafePreferences.h" +#import "SULog.h" + @implementation SUHost @@ -31,7 +34,7 @@ - (void)dealloc [super dealloc]; } -- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [self bundlePath]]; } +- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self bundlePath], [self installationPath]]; } - (NSBundle *)bundle { @@ -43,6 +46,15 @@ - (NSString *)bundlePath return [bundle bundlePath]; } +- (NSString *)installationPath +{ +#if 1 + return [[[bundle bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent: [NSString stringWithFormat: @"%@.%@", [bundle objectForInfoDictionaryKey:@"CFBundleName"], [[bundle bundlePath] pathExtension]]]; +#else + return [bundle bundlePath]; +#endif +} + - (NSString *)name { NSString *name = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]; @@ -115,7 +127,7 @@ - (NSString *)publicDSAKey // More likely, we've got a reference to a Resources file by filename: NSString *keyFilename = [self objectForInfoDictionaryKey:SUPublicDSAKeyFileKey]; if (!keyFilename) { return nil; } - NSError *ignoreErr; + NSError *ignoreErr = nil; return [NSString stringWithContentsOfFile:[bundle pathForResource:keyFilename ofType:nil] encoding:NSASCIIStringEncoding error: &ignoreErr]; } @@ -142,7 +154,7 @@ - (id)objectForUserDefaultsKey:(NSString *)defaultName if (bundle == [NSBundle mainBundle]) return [[NSUserDefaults standardUserDefaults] objectForKey:defaultName]; - CFPropertyListRef obj = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]); + 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 @@ -159,8 +171,8 @@ - (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName; } else { - CFPreferencesSetValue((CFStringRef)defaultName, value, (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); - CFPreferencesSynchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + ThreadSafePreferences_SetValue((CFStringRef)defaultName, value, (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + ThreadSafePreferences_Synchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); } } @@ -170,7 +182,7 @@ - (BOOL)boolForUserDefaultsKey:(NSString *)defaultName return [[NSUserDefaults standardUserDefaults] boolForKey:defaultName]; BOOL value; - CFPropertyListRef plr = CFPreferencesCopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]); + CFPropertyListRef plr = ThreadSafePreferences_CopyAppValue((CFStringRef)defaultName, (CFStringRef)[bundle bundleIdentifier]); if (plr == NULL) value = NO; else @@ -190,8 +202,8 @@ - (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName } else { - CFPreferencesSetValue((CFStringRef)defaultName, (CFBooleanRef)[NSNumber numberWithBool:value], (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); - CFPreferencesSynchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + ThreadSafePreferences_SetValue((CFStringRef)defaultName, (CFBooleanRef)[NSNumber numberWithBool:value], (CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + ThreadSafePreferences_Synchronize((CFStringRef)[bundle bundleIdentifier], kCFPreferencesCurrentUser, kCFPreferencesAnyHost); } } diff --git a/SUInstaller.h b/SUInstaller.h index 6f12aefd6..b35c4a76a 100644 --- a/SUInstaller.h +++ b/SUInstaller.h @@ -16,6 +16,8 @@ @interface SUInstaller : NSObject { } + (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id )comparator; + (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate; + ++ (NSString*)updateFolder; @end @interface NSObject (SUInstallerDelegateInformalProtocol) diff --git a/SUInstaller.m b/SUInstaller.m index dd8fa73b3..69bae86f7 100644 --- a/SUInstaller.m +++ b/SUInstaller.m @@ -11,9 +11,18 @@ #import "SUPackageInstaller.h" #import "SUHost.h" #import "SUConstants.h" +#import "SULog.h" + @implementation SUInstaller +static NSString* sUpdateFolder = nil; + ++(NSString*) updateFolder +{ + return sUpdateFolder; +} + + (BOOL)_isAliasFolderAtPath:(NSString *)path { FSRef fileRef; @@ -34,19 +43,23 @@ + (BOOL)_isAliasFolderAtPath:(NSString *)path } -+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id )comparator ++ (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id )comparator { // Search subdirectories for the application - NSString *currentFile, *newAppDownloadPath = nil, + NSString *currentFile, + *newAppDownloadPath = nil, *bundleFileName = [[host bundlePath] lastPathComponent], - *alternateBundleFileName = [[host name] stringByAppendingPathExtension: [[host bundlePath] pathExtension]]; + *alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]]; BOOL isPackage = NO; NSString *fallbackPackagePath = nil; - NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath: updateFolder]; + NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath: inUpdateFolder]; + + [sUpdateFolder release]; + sUpdateFolder = [inUpdateFolder retain]; while ((currentFile = [dirEnum nextObject])) { - NSString *currentPath = [updateFolder stringByAppendingPathComponent:currentFile]; + NSString *currentPath = [inUpdateFolder stringByAppendingPathComponent:currentFile]; if ([[currentFile lastPathComponent] isEqualToString:bundleFileName] || [[currentFile lastPathComponent] isEqualToString:alternateBundleFileName]) // We found one! { @@ -106,9 +119,11 @@ + (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host + (void)_mdimportHost:(SUHost *)host { + // *** GETS CALLED ON NON-MAIN THREAD! + NSTask *mdimport = [[[NSTask alloc] init] autorelease]; [mdimport setLaunchPath:@"/usr/bin/mdimport"]; - [mdimport setArguments:[NSArray arrayWithObject:[host bundlePath]]]; + [mdimport setArguments:[NSArray arrayWithObject:[host installationPath]]]; @try { [mdimport launch]; @@ -116,23 +131,36 @@ + (void)_mdimportHost:(SUHost *)host @catch (NSException * launchException) { // No big deal. - NSLog(@"Sparkle Error: %@", [launchException description]); + SULog(@"Sparkle Error: %@", [launchException description]); } } + +#define SUNotifyDictHostKey @"SUNotifyDictHost" +#define SUNotifyDictErrorKey @"SUNotifyDictError" +#define SUNotifyDictDelegateKey @"SUNotifyDictDelegate" + + (void)_finishInstallationWithResult:(BOOL)result host:(SUHost *)host error:(NSError *)error delegate:delegate { + // *** GETS CALLED ON NON-MAIN THREAD! + if (result == YES) { [self _mdimportHost:host]; if ([delegate respondsToSelector:@selector(installerFinishedForHost:)]) - [delegate installerFinishedForHost:host]; + [delegate performSelectorOnMainThread: @selector(installerFinishedForHost:) withObject: host waitUntilDone: NO]; } else { if ([delegate respondsToSelector:@selector(installerForHost:failedWithError:)]) - [delegate installerForHost:host failedWithError:error]; + [self performSelectorOnMainThread: @selector(notifyDelegateOfFailure:) withObject: [NSDictionary dictionaryWithObjectsAndKeys: host, SUNotifyDictHostKey, error, SUNotifyDictErrorKey, delegate, SUNotifyDictDelegateKey, nil] waitUntilDone: NO]; } } + ++(void) notifyDelegateOfFailure: (NSDictionary*)dict +{ + [[dict objectForKey: SUNotifyDictDelegateKey] installerForHost: [dict objectForKey: SUNotifyDictHostKey] failedWithError: [dict objectForKey: SUNotifyDictErrorKey]]; +} + @end diff --git a/SULog.h b/SULog.h new file mode 100644 index 000000000..7631f95ef --- /dev/null +++ b/SULog.h @@ -0,0 +1,31 @@ +/* + * SULog.h + * EyeTV + * + * Created by Uli Kusterer on 12/03/2009. + * Copyright 2008 Elgato Systems GmbH. All rights reserved. + * + */ + +/* + Log output for troubleshooting Sparkle failures on end-user machines. + Your tech support will hug you if you tell them about this. +*/ + +#pragma once + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#include + + +// ----------------------------------------------------------------------------- +// Prototypes: +// ----------------------------------------------------------------------------- + +void SUClearLog( void ); +void SULog( NSString* format, ... ); + + diff --git a/SULog.m b/SULog.m new file mode 100644 index 000000000..b58235aa7 --- /dev/null +++ b/SULog.m @@ -0,0 +1,80 @@ +/* + * SULog.m + * EyeTV + * + * Created by Uli Kusterer on 12/03/2009. + * Copyright 2009 Elgato Systems GmbH. All rights reserved. + * + */ + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#include "SULog.h" + + +// ----------------------------------------------------------------------------- +// Constants: +// ----------------------------------------------------------------------------- + +#define LOG_FILE_PATH @"~/Library/Logs/SparkleUpdateLog.log" + + +// ----------------------------------------------------------------------------- +// SUClearLog: +// Erase the log at the start of an update. We don't want to litter the +// user's hard disk with logging data that's mostly unused, so each app +// should clear the log before it starts updating, so only the most recent +// update is kept around. +// +// TAKES: +// sender - Object that sent this message, typically of type X. +// +// GIVES: +// param - who owns the returned value? +// result - same here. +// ----------------------------------------------------------------------------- + +void SUClearLog( void ) +{ + FILE* logfile = fopen([[LOG_FILE_PATH stringByExpandingTildeInPath] fileSystemRepresentation],"w"); + if( logfile ) + fclose(logfile); + else + NSLog(@"----- Sparkle Log -----"); +} + + +// ----------------------------------------------------------------------------- +// SULog: +// Like NSLog, but logs to one specific log file. Each line is prefixed +// with the current date and time, to help in regressing issues. +// +// TAKES: +// format - NSLog/printf-style format string. +// ... - More parameters depending on format string's contents. +// ----------------------------------------------------------------------------- + +void SULog( NSString* format, ... ) +{ + va_list ap; + va_start(ap, format); + NSString* theStr = [[[NSString alloc] initWithFormat: format arguments: ap] autorelease]; + FILE* logfile = fopen([[LOG_FILE_PATH stringByExpandingTildeInPath] fileSystemRepresentation],"a"); + if( !logfile ) + NSLog( @"%@",theStr ); + else + { + theStr = [NSString stringWithFormat: @"%@: %@", [NSDate date], theStr]; + NSData* theData = [theStr dataUsingEncoding: NSUTF8StringEncoding]; + char newlineChar = '\n'; + fwrite( [theData bytes], 1, [theData length], logfile ); + fwrite( &newlineChar, 1, 1, logfile ); // Append a newline. + fclose( logfile ); + logfile = NULL; + } + va_end(ap); +} + + diff --git a/SUPackageInstaller.h b/SUPackageInstaller.h index b31bc59dc..5b69095f2 100644 --- a/SUPackageInstaller.h +++ b/SUPackageInstaller.h @@ -9,7 +9,11 @@ #ifndef SUPACKAGEINSTALLER_H #define SUPACKAGEINSTALLER_H -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUPlainInstaller.h" @interface SUPackageInstaller : SUPlainInstaller { } diff --git a/SUPackageInstaller.m b/SUPackageInstaller.m index 93c26d982..7be44994e 100644 --- a/SUPackageInstaller.m +++ b/SUPackageInstaller.m @@ -7,6 +7,7 @@ // #import "SUPackageInstaller.h" +#import #import "SUConstants.h" #ifndef NSAppKitVersionNumber10_4 diff --git a/SUPipedUnarchiver.m b/SUPipedUnarchiver.m index db7ffc99a..c216f44c5 100644 --- a/SUPipedUnarchiver.m +++ b/SUPipedUnarchiver.m @@ -8,6 +8,8 @@ #import "SUPipedUnarchiver.h" #import "SUUnarchiver_Private.h" +#import "SULog.h" + @implementation SUPipedUnarchiver @@ -18,7 +20,7 @@ + (SEL)_selectorConformingToTypeOfPath:(NSString *)path typeSelectorDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:@"_extractZIP", @".zip", @"_extractTAR", @".tar", @"_extractTGZ", @".tar.gz", @"_extractTGZ", @".tgz", @"_extractTBZ", @".tar.bz2", @"_extractTBZ", @".tbz", nil] retain]; - + NSString *lastPathComponent = [path lastPathComponent]; NSEnumerator *typeEnumerator = [typeSelectorDictionary keyEnumerator]; id currentType; @@ -44,6 +46,10 @@ + (BOOL)_canUnarchivePath:(NSString *)path // This method abstracts the types that use a command line tool piping data from stdin. - (void)_extractArchivePipingDataToCommand:(NSString *)command { + // *** GETS CALLED ON NON-MAIN THREAD!!! + + SULog(@"Extracting %@ using '%@'",archivePath,command); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; FILE *fp = NULL, *cmdFP = NULL; @@ -97,21 +103,29 @@ - (void)_extractArchivePipingDataToCommand:(NSString *)command - (void)_extractTAR { + // *** GETS CALLED ON NON-MAIN THREAD!!! + return [self _extractArchivePipingDataToCommand:@"tar -xC \"$DESTINATION\""]; } - (void)_extractTGZ { + // *** GETS CALLED ON NON-MAIN THREAD!!! + return [self _extractArchivePipingDataToCommand:@"tar -zxC \"$DESTINATION\""]; } - (void)_extractTBZ { + // *** GETS CALLED ON NON-MAIN THREAD!!! + return [self _extractArchivePipingDataToCommand:@"tar -jxC \"$DESTINATION\""]; } - (void)_extractZIP { + // *** GETS CALLED ON NON-MAIN THREAD!!! + return [self _extractArchivePipingDataToCommand:@"ditto -x -k - \"$DESTINATION\""]; } diff --git a/SUPlainInstaller.h b/SUPlainInstaller.h index 547472830..2172e82ce 100644 --- a/SUPlainInstaller.h +++ b/SUPlainInstaller.h @@ -9,11 +9,16 @@ #ifndef SUPLAININSTALLER_H #define SUPLAININSTALLER_H -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUInstaller.h" #import "SUVersionComparisonProtocol.h" @class SUHost; + @interface SUPlainInstaller : SUInstaller { } + (void)performInstallationWithPath:(NSString *)path host:(SUHost *)host delegate:delegate synchronously:(BOOL)synchronously versionComparator:(id )comparator; @end diff --git a/SUPlainInstaller.m b/SUPlainInstaller.m index 59f36a2df..5edce009f 100644 --- a/SUPlainInstaller.m +++ b/SUPlainInstaller.m @@ -9,6 +9,8 @@ #import "SUPlainInstaller.h" #import "SUPlainInstallerInternals.h" #import "SUConstants.h" +#import "SUHost.h" + NSString *SUInstallerPathKey = @"SUInstallerPath"; NSString *SUInstallerTargetPathKey = @"SUInstallerTargetPath"; @@ -22,11 +24,15 @@ @implementation SUPlainInstaller + (void)_finishInstallationWithInfo:(NSDictionary *)info { + // *** GETS CALLED ON NON-MAIN THREAD! + [self _finishInstallationWithResult:[[info objectForKey:SUInstallerResultKey] boolValue] host:[info objectForKey:SUInstallerHostKey] error:[info objectForKey:SUInstallerErrorKey] delegate:[info objectForKey:SUInstallerDelegateKey]]; } + (void)_performInstallationWithInfo:(NSDictionary *)info { + // *** GETS CALLED ON NON-MAIN THREAD! + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSError *error = nil; diff --git a/SUPlainInstallerInternals.m b/SUPlainInstallerInternals.m index dbe3ef92e..3111e790c 100644 --- a/SUPlainInstallerInternals.m +++ b/SUPlainInstallerInternals.m @@ -6,7 +6,11 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUPlainInstallerInternals.h" #import "SUConstants.h" @@ -16,6 +20,8 @@ #import #import #import +#import + @interface SUPlainInstaller (MMExtendedAttributes) // Removes the directory tree rooted at |root| from the file quarantine. @@ -43,6 +49,8 @@ + (void)releaseFromQuarantine:(NSString*)root; static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authorization, const char* executablePath, AuthorizationFlags options, const char* const* arguments) { + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL); BOOL returnValue = YES; @@ -86,8 +94,67 @@ + (NSString *)temporaryNameForPath:(NSString *)path return [tempDir lastPathComponent]; } ++ (NSString *)_temporaryCopyNameForPath:(NSString *)path didFindTrash: (BOOL*)outDidFindTrash +{ + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + NSString *tempDir = nil; + + UInt8 trashPath[MAXPATHLEN +1] = { 0 }; + FSRef trashRef, pathRef; + FSVolumeRefNum vSrcRefNum = kFSInvalidVolumeRefNum; + FSCatalogInfo catInfo = { 0 }; + OSStatus err = FSPathMakeRef( (UInt8*) [path fileSystemRepresentation], &pathRef, NULL ); + if( err == noErr ) + { + err = FSGetCatalogInfo( &pathRef, kFSCatInfoVolume, &catInfo, NULL, NULL, NULL ); + vSrcRefNum = catInfo.volume; + } + if( err == noErr ) + err = FSFindFolder( vSrcRefNum, kTrashFolderType, kCreateFolder, &trashRef ); + if( err == noErr ) + err = FSGetCatalogInfo( &trashRef, kFSCatInfoVolume, &catInfo, NULL, NULL, NULL ); + if( err == noErr && vSrcRefNum != catInfo.volume ) + err = nsvErr; // Couldn't find a trash folder on same volume as given path. Docs say this may happen in the future. + if( err == noErr ) + err = FSRefMakePath( &trashRef, trashPath, MAXPATHLEN ); + if( err == noErr ) + tempDir = [NSString stringWithUTF8String: (char*) trashPath]; + if( outDidFindTrash ) + *outDidFindTrash = (tempDir != nil); + if( !tempDir ) + tempDir = [path stringByDeletingLastPathComponent]; + + // Let's try to read the version number so the filename will be more meaningful. + NSString *postFix = nil; + NSString *version = nil; + if ((version = [[NSBundle bundleWithPath: path] objectForInfoDictionaryKey:@"CFBundleVersion"]) && ![version isEqualToString:@""]) + { + // We'll clean it up a little for safety. + // The cast is necessary because of a bug in the headers in pre-10.5 SDKs + NSMutableCharacterSet *validCharacters = (id)[NSMutableCharacterSet alphanumericCharacterSet]; + [validCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-()"]]; + postFix = [version stringByTrimmingCharactersInSet:[validCharacters invertedSet]]; + } + else + postFix = @"old"; + NSString *prefix = [NSString stringWithFormat: @"%@ (%@)", [[path lastPathComponent] stringByDeletingPathExtension], postFix]; + NSString *tempName = [prefix stringByAppendingPathExtension: [path pathExtension]]; + tempDir = [tempDir stringByAppendingPathComponent: tempName]; + + // Now let's make sure we get a unique path. + int cnt=2; + while ([[NSFileManager defaultManager] fileExistsAtPath:tempDir] && cnt <= 9999) + tempDir = [[tempDir stringByDeletingLastPathComponent] stringByAppendingPathComponent: [NSString stringWithFormat:@"%@ %d.%@", prefix, cnt++, [path pathExtension]]]; + + return tempDir; +} + + (BOOL)_copyPathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst temporaryPath:(NSString *)tmp error:(NSError **)error { + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + + //BOOL foundTrash = NO; // +++ Using trash as tmp folder to begin with would be way cooler, IMHO. + //NSString* tmp = [self _temporaryCopyNameForPath:dst didFindTrash: &foundTrash]; const char* srcPath = [src fileSystemRepresentation]; const char* tmpPath = [tmp fileSystemRepresentation]; const char* dstPath = [dst fileSystemRepresentation]; @@ -112,35 +179,35 @@ + (BOOL)_copyPathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst snprintf(uidgid, sizeof(uidgid), "%d:%d", dstSB.st_uid, dstSB.st_gid); - const char* executables[] = { - "/bin/rm", - "/bin/mv", - "/bin/mv", - "/bin/rm", - NULL, // pause here and do some housekeeping before - // continuing - "/usr/sbin/chown", - NULL // stop here for real - }; + if( res ) // Set permissions while it's still in source, so we have it with working and correct perms when it arrives at destination. + { + const char* coParams[] = { "-R", uidgid, srcPath, NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/usr/sbin/chown", kAuthorizationFlagDefaults, coParams ); + } - // 4 is the maximum number of arguments to any command, - // including the NULL that signals the end of an argument - // list. - const char* const argumentLists[][4] = { - { "-rf", tmpPath, NULL }, // make room for the temporary file... this is kinda unsafe; should probably do something better. - { "-f", dstPath, tmpPath, NULL }, // mv - { "-f", srcPath, dstPath, NULL }, // mv - { "-rf", tmpPath, NULL }, // rm - { NULL }, // pause - { "-R", uidgid, dstPath, NULL }, // chown - { NULL } // stop - }; + BOOL haveDst = [[NSFileManager defaultManager] fileExistsAtPath: dst]; + if( res && haveDst ) // If there's something at our tmp path (previous failed update or whatever) delete that first. + { + const char* rmParams[] = { "-rf", tmpPath, NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams ); + } - // Process the commands up until the first NULL - int commandIndex = 0; - for (; executables[commandIndex] != NULL; ++commandIndex) { - if (res) - res = AuthorizationExecuteWithPrivilegesAndWait(auth, executables[commandIndex], kAuthorizationFlagDefaults, argumentLists[commandIndex]); + if( res && haveDst ) // Move old exe to tmp path. + { + const char* mvParams[] = { "-f", dstPath, tmpPath, NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/mv", kAuthorizationFlagDefaults, mvParams ); + } + + if( res ) // Move new exe to old exe's path. + { + const char* mvParams2[] = { "-f", srcPath, dstPath, NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/mv", kAuthorizationFlagDefaults, mvParams2 ); + } + + if( res && haveDst /*&& !foundTrash*/ ) // If we managed to put the old exe in the trash, leave it there for the user to delete or recover. + { // ... Otherwise we better delete it, wouldn't want dozens of old versions lying around next to the new one. + const char* rmParams2[] = { "-rf", tmpPath, NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams2 ); } // If the currently-running application is trusted, the new @@ -159,15 +226,6 @@ + (BOOL)_copyPathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst [self performSelectorOnMainThread:@selector(releaseFromQuarantine:) withObject:dst waitUntilDone:YES]; } - // Now move past the NULL we found and continue executing - // commands from the list. - ++commandIndex; - - for (; executables[commandIndex] != NULL; ++commandIndex) { - if (res) - res = AuthorizationExecuteWithPrivilegesAndWait(auth, executables[commandIndex], kAuthorizationFlagDefaults, argumentLists[commandIndex]); - } - AuthorizationFree(auth, 0); if (!res) @@ -269,6 +327,8 @@ + (int)removeXAttr:(const char*)name fromFile:(NSString*)file options:(int)options { + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + typedef int (*removexattr_type)(const char*, const char*, int); // Reference removexattr directly, it's in the SDK. static removexattr_type removexattr_func = removexattr; @@ -300,6 +360,8 @@ + (int)removeXAttr:(const char*)name + (void)releaseFromQuarantine:(NSString*)root { + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + const char* quarantineAttribute = "com.apple.quarantine"; const int removeXAttrOptions = XATTR_NOFOLLOW; diff --git a/SUScheduledUpdateDriver.m b/SUScheduledUpdateDriver.m index 2c92dc855..2d4b07502 100644 --- a/SUScheduledUpdateDriver.m +++ b/SUScheduledUpdateDriver.m @@ -7,7 +7,11 @@ // #import "SUScheduledUpdateDriver.h" -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" @implementation SUScheduledUpdateDriver diff --git a/SUStandardVersionComparator.m b/SUStandardVersionComparator.m index 03c0c0d70..c2066a0c8 100644 --- a/SUStandardVersionComparator.m +++ b/SUStandardVersionComparator.m @@ -6,7 +6,11 @@ // Copyright 2007 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUStandardVersionComparator.h" @implementation SUStandardVersionComparator @@ -22,15 +26,19 @@ + (SUStandardVersionComparator *)defaultComparator typedef enum { kNumberType, kStringType, - kPeriodType + kSeparatorType, } SUCharacterType; - (SUCharacterType)typeOfCharacter:(NSString *)character { if ([character isEqualToString:@"."]) { - return kPeriodType; + return kSeparatorType; } else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[character characterAtIndex:0]]) { return kNumberType; + } else if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[character characterAtIndex:0]]) { + return kSeparatorType; + } else if ([[NSCharacterSet punctuationCharacterSet] characterIsMember:[character characterAtIndex:0]]) { + return kSeparatorType; } else { return kStringType; } @@ -52,7 +60,7 @@ - (NSArray *)splitVersionString:(NSString *)version for (i = 1; i <= n; ++i) { character = [version substringWithRange:NSMakeRange(i, 1)]; newType = [self typeOfCharacter:character]; - if (oldType != newType || oldType == kPeriodType) { + if (oldType != newType || oldType == kSeparatorType) { // We've reached a new segment NSString *aPart = [[NSString alloc] initWithString:s]; [parts addObject:aPart]; diff --git a/SUStatus.nib/classes.nib b/SUStatus.nib/classes.nib new file mode 100644 index 000000000..22f13f8b6 --- /dev/null +++ b/SUStatus.nib/classes.nib @@ -0,0 +1,56 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUStatusController + LANGUAGE + ObjC + OUTLETS + + actionButton + NSButton + progressBar + NSProgressIndicator + + SUPERCLASS + SUWindowController + + + IBVersion + 1 + + diff --git a/SUStatus.nib/info.nib b/SUStatus.nib/info.nib new file mode 100644 index 000000000..a9ac8673c --- /dev/null +++ b/SUStatus.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/SUStatusController.m b/SUStatusController.m index 16774c0a3..3d0d7f7ca 100644 --- a/SUStatusController.m +++ b/SUStatusController.m @@ -6,8 +6,14 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUStatusController.h" +#import "SUHost.h" + @implementation SUStatusController @@ -31,7 +37,7 @@ - (void)dealloc [super dealloc]; } -- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; } +- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [host bundlePath], [host installationPath]]; } - (void)awakeFromNib { @@ -60,7 +66,7 @@ - (void)beginActionWithTitle:(NSString *)aTitle maxProgressValue:(double)aMaxPro [self setStatusText:aStatusText]; } -- (void)setButtonTitle:(NSString *)aButtonTitle target:target action:(SEL)action isDefault:(BOOL)isDefault +- (void)setButtonTitle:(NSString *)aButtonTitle target: (id)target action:(SEL)action isDefault:(BOOL)isDefault { [self willChangeValueForKey:@"buttonTitle"]; if (buttonTitle != aButtonTitle) @@ -82,6 +88,9 @@ - (void)setButtonTitle:(NSString *)aButtonTitle target:target action:(SEL)action [actionButton setTarget:target]; [actionButton setAction:action]; [actionButton setKeyEquivalent:isDefault ? @"\r" : @""]; + + // 06/05/2008 Alex: Avoid a crash when cancelling during the extraction + [self setButtonEnabled:(target != NULL)]; } - (BOOL)progressBarShouldAnimate diff --git a/SUSystemProfiler.h b/SUSystemProfiler.h index 6238e39cc..0835087d2 100644 --- a/SUSystemProfiler.h +++ b/SUSystemProfiler.h @@ -9,6 +9,8 @@ #ifndef SUSYSTEMPROFILER_H #define SUSYSTEMPROFILER_H +#import + @class SUHost; @interface SUSystemProfiler : NSObject {} + (SUSystemProfiler *)sharedSystemProfiler; diff --git a/SUUIBasedUpdateDriver.h b/SUUIBasedUpdateDriver.h index 09a96472c..1d1d478ea 100644 --- a/SUUIBasedUpdateDriver.h +++ b/SUUIBasedUpdateDriver.h @@ -13,7 +13,9 @@ #import "SUBasicUpdateDriver.h" @class SUStatusController, SUUpdateAlert; -@interface SUUIBasedUpdateDriver : SUBasicUpdateDriver { + +@interface SUUIBasedUpdateDriver : SUBasicUpdateDriver +{ SUStatusController *statusController; SUUpdateAlert *updateAlert; } diff --git a/SUUIBasedUpdateDriver.m b/SUUIBasedUpdateDriver.m index 3fe001edd..132797b9e 100644 --- a/SUUIBasedUpdateDriver.m +++ b/SUUIBasedUpdateDriver.m @@ -11,6 +11,7 @@ #import "SUUpdateAlert.h" #import "SUHost.h" #import "SUStatusController.h" +#import "SUConstants.h" @implementation SUUIBasedUpdateDriver @@ -66,9 +67,17 @@ - (void)updateAlert:(SUUpdateAlert *)alert finishedWithChoice:(SUUpdateAlertChoi [statusController showWindow:self]; [self downloadUpdate]; break; - + + case SUOpenInfoURLChoice: + [[NSWorkspace sharedWorkspace] openURL: [updateItem infoURL]]; + [self abortUpdate]; + break; + case SUSkipThisVersionChoice: [host setObject:[updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey]; + [self abortUpdate]; + break; + case SURemindMeLaterChoice: [self abortUpdate]; break; @@ -80,6 +89,7 @@ - (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)r [statusController setMaxProgressValue:[response expectedContentLength]]; } + - (NSString *)_humanReadableSizeFromDouble:(double)value { if (value < 1024) @@ -139,7 +149,11 @@ - (void)unarchiverDidFinish:(SUUnarchiver *)ua [NSApp requestUserAttention:NSInformationalRequest]; } -- (void)installAndRestart:sender { [self installUpdate]; } +- (void)installAndRestart: (id)sender +{ + if( [updater mayUpdateAndRestart] ) + [self installUpdate]; +} - (void)installUpdate { diff --git a/SUUnarchiver.m b/SUUnarchiver.m index 03cfaac0d..3c4a803ec 100644 --- a/SUUnarchiver.m +++ b/SUUnarchiver.m @@ -7,7 +7,11 @@ // -#import "Sparkle.h" +#import "SUUpdater.h" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUVersionComparisonProtocol.h" #import "SUUnarchiver.h" #import "SUUnarchiver_Private.h" diff --git a/SUUpdateAlert.h b/SUUpdateAlert.h index 1602b4a6a..5530c75ae 100644 --- a/SUUpdateAlert.h +++ b/SUUpdateAlert.h @@ -15,7 +15,8 @@ typedef enum { SUInstallUpdateChoice, SURemindMeLaterChoice, - SUSkipThisVersionChoice + SUSkipThisVersionChoice, + SUOpenInfoURLChoice } SUUpdateAlertChoice; @class WebView, SUAppcastItem, SUHost; @@ -26,6 +27,7 @@ typedef enum IBOutlet WebView *releaseNotesView; IBOutlet NSTextField *description; + IBOutlet NSButton *installButton; // UK 2007-08-31. NSProgressIndicator *releaseNotesSpinner; BOOL webViewFinishedLoading; } @@ -41,6 +43,7 @@ typedef enum @interface NSObject (SUUpdateAlertDelegate) - (void)updateAlert:(SUUpdateAlert *)updateAlert finishedWithChoice:(SUUpdateAlertChoice)updateChoice; +- (void)updateAlert:(SUUpdateAlert *)updateAlert shouldAllowAutoUpdate: (BOOL*)shouldAllowAutoUpdate; @end #endif diff --git a/SUUpdateAlert.m b/SUUpdateAlert.m index 08fa27927..38b8d6e94 100644 --- a/SUUpdateAlert.m +++ b/SUUpdateAlert.m @@ -6,11 +6,25 @@ // Copyright 2006 Andy Matuschak. All rights reserved. // +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + #import "SUUpdateAlert.h" #import "SUHost.h" #import +#import "SUConstants.h" + + +@interface WebView (SUTenFiveProperty) + +-(void) setDrawsBackground: (BOOL)state; + +@end + + @implementation SUUpdateAlert - (id)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)aHost @@ -50,6 +64,11 @@ - (IBAction)installUpdate:sender [self endWithSelection:SUInstallUpdateChoice]; } +- (IBAction)openInfoURL:sender +{ + [self endWithSelection:SUOpenInfoURLChoice]; +} + - (IBAction)skipThisVersion:sender { [self endWithSelection:SUSkipThisVersionChoice]; @@ -99,26 +118,40 @@ - (BOOL)showsReleaseNotes { NSNumber *shouldShowReleaseNotes = [host objectForInfoDictionaryKey:SUShowReleaseNotesKey]; if (shouldShowReleaseNotes == nil) - return YES; // defaults to YES + { + // UK 2007-09-18: Don't show release notes if RSS item contains no description and no release notes URL: + return( ([updateItem itemDescription] != nil + && [[[updateItem itemDescription] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0) + || [updateItem releaseNotesURL] != nil ); + } else return [shouldShowReleaseNotes boolValue]; } - (BOOL)allowsAutomaticUpdates { - if (![host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey]) - return YES; // defaults to YES - return [host boolForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey]; + BOOL allowAutoUpdates = YES; // Defaults to YES. + if( [host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey] ) + allowAutoUpdates = [host boolForInfoDictionaryKey: SUAllowsAutomaticUpdatesKey]; + + // UK 2007-08-31: Give delegate a chance to modify this choice: + if( delegate && [delegate respondsToSelector: @selector(updateAlert:shouldAllowAutoUpdate:)] ) + [delegate updateAlert: self shouldAllowAutoUpdate: &allowAutoUpdates]; + + return allowAutoUpdates; } - (void)awakeFromNib { - [[self window] setLevel:NSFloatingWindowLevel]; + NSString* sizeStr = [host objectForInfoDictionaryKey:SUFixedHTMLDisplaySizeKey]; + + //[[self window] setLevel:NSFloatingWindowLevel]; // This means the window will float over all other apps, if our app is switched out ?! UK 2007-09-04 + [[self window] setFrameAutosaveName: sizeStr ? @"" : @"SUUpdateAlertFrame"]; // We're gonna do some frame magic to match the window's size to the description field and the presence of the release notes view. - NSRect frame = [[self window] frame]; - - if (![self showsReleaseNotes]) + NSRect frame = [[self window] frame]; + BOOL showReleaseNotes = [self showsReleaseNotes]; // UK 2007-09-18 + if (!showReleaseNotes) // UK 2007-09-18 { // Resize the window to be appropriate for not having a huge release notes view. frame.size.height -= [releaseNotesView frame].size.height + 40; // Extra 40 is for the release notes label and margin. @@ -133,15 +166,52 @@ - (void)awakeFromNib [[[releaseNotesView superview] superview] setFrame:boxFrame]; } + if( showReleaseNotes ) // UK 2007-09-18 (whole block) + { + if( sizeStr ) + { + NSSize desiredSize = NSSizeFromString( sizeStr ); + NSSize sizeDiff = NSZeroSize; + NSBox* boxView = (NSBox*)[[releaseNotesView superview] superview]; + + //[boxView setBorderType: NSNoBorder]; + [releaseNotesView setDrawsBackground: NO]; + + sizeDiff.width = desiredSize.width -[releaseNotesView frame].size.width; + sizeDiff.height = desiredSize.height -[releaseNotesView frame].size.height; + frame.size.width += sizeDiff.width; + frame.size.height += sizeDiff.height; + + // No resizing: + [[self window] setShowsResizeIndicator:NO]; + [[self window] setMinSize:frame.size]; + [[self window] setMaxSize:frame.size]; + } + } + [[self window] setFrame:frame display:NO]; [[self window] center]; - if ([self showsReleaseNotes]) + if (showReleaseNotes) // UK 2007-09-18 { [self displayReleaseNotes]; } + + [[[releaseNotesView superview] superview] setHidden: !showReleaseNotes]; // UK 2007-09-18 + + if( [updateItem fileURL] == nil ) // UK 2007-08-31 (whole if clause) + { + [installButton setTitle: SULocalizedString( @"Learn More...", @"Alternate title for 'Install Update' button." )]; + [installButton setAction: @selector(openInfoURL:)]; + } } +-(BOOL)showsReleaseNotesText +{ + return( [host objectForInfoDictionaryKey:SUFixedHTMLDisplaySizeKey] == nil ); +} + + - (BOOL)windowShouldClose:note { [self endWithSelection:SURemindMeLaterChoice]; diff --git a/SUUpdateDriver.m b/SUUpdateDriver.m index a3ccdd322..66b5880f9 100644 --- a/SUUpdateDriver.m +++ b/SUUpdateDriver.m @@ -7,6 +7,7 @@ // #import "SUUpdateDriver.h" +#import "SUHost.h" NSString *SUUpdateDriverFinishedNotification = @"SUUpdateDriverFinished"; @@ -18,7 +19,7 @@ @implementation SUUpdateDriver return self; } -- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; } +- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [host bundlePath], [host installationPath]]; } - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)h { diff --git a/SUUpdatePermissionPrompt.m b/SUUpdatePermissionPrompt.m index a6277a505..0a4106fcc 100644 --- a/SUUpdatePermissionPrompt.m +++ b/SUUpdatePermissionPrompt.m @@ -9,6 +9,7 @@ #import "SUUpdatePermissionPrompt.h" #import "SUHost.h" +#import "SUConstants.h" @implementation SUUpdatePermissionPrompt diff --git a/SUUpdater.h b/SUUpdater.h index 0ed3a2d42..140e4fad9 100644 --- a/SUUpdater.h +++ b/SUUpdater.h @@ -9,7 +9,7 @@ #ifndef SUUPDATER_H #define SUUPDATER_H -#import +#import "SUVersionComparisonProtocol.h" @class SUUpdateDriver, SUAppcastItem, SUHost, SUAppcast; @interface SUUpdater : NSObject { @@ -37,7 +37,7 @@ - (NSTimeInterval)updateCheckInterval; - (void)setFeedURL:(NSURL *)feedURL; -- (NSURL *)feedURL; +- (NSURL *)feedURL; // *** MUST BE CALLED ON MAIN THREAD *** - (void)setUserAgentString:(NSString *)userAgent; - (NSString *)userAgentString; @@ -50,7 +50,7 @@ // This IBAction is meant for a main menu item. Hook up any menu item to this action, // and Sparkle will check for updates and report back its findings verbosely. -- (IBAction)checkForUpdates:sender; +- (IBAction)checkForUpdates:(id)sender; // This kicks off an update meant to be programmatically initiated. That is, it will display no UI unless it actually finds an update, // in which case it proceeds as usual. If the fully automated updating is turned on, however, this will invoke that behavior, and if an @@ -68,12 +68,22 @@ - (void)resetUpdateCycle; - (BOOL)updateInProgress; + +-(BOOL) mayUpdateAndRestart; // If we can't restart, don't update, because that'd mean anything the old app (still running) reads from disk + @end @interface NSObject (SUUpdaterDelegateInformalProtocol) + +// Use this to keep Sparkle from popping up e.g. while your setup assistant is showing: +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle; + // This method allows you to add extra parameters to the appcast URL, potentially based on whether or not Sparkle will also be sending along the system profile. This method should return an array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. - (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; +// If you need to generate the whole URL: +-(NSString*) feedURLStringForUpdater: (SUUpdater*)updater; + // Use this to override the default behavior for Sparkle prompting the user about automatic update checks. - (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)bundle; @@ -94,8 +104,14 @@ - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update; // Return YES to delay the relaunch until you do some processing; invoke the given NSInvocation to continue. +// This is not called if the user didn't relaunch on the previous update, in that case it will immediately +// restart. - (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation; +// Some apps *can not* be relaunched in certain circumstances. They can use this method +// to prevent a relaunch "hard": +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; + // Called immediately before relaunching. - (void)updaterWillRelaunchApplication:(SUUpdater *)updater; @@ -109,13 +125,13 @@ @end // Define some minimum intervals to avoid DOS-like checking attacks. These are in seconds. -#ifdef DEBUG +#ifdef DEBUG && 0 #define SU_MIN_CHECK_INTERVAL 60 #else #define SU_MIN_CHECK_INTERVAL 60*60 #endif -#ifdef DEBUG +#ifdef DEBUG && 0 #define SU_DEFAULT_CHECK_INTERVAL 60 #else #define SU_DEFAULT_CHECK_INTERVAL 60*60*24 diff --git a/SUUpdater.m b/SUUpdater.m index 16291250b..acc490e68 100644 --- a/SUUpdater.m +++ b/SUUpdater.m @@ -15,9 +15,13 @@ #import "SUProbingUpdateDriver.h" #import "SUUserInitiatedUpdateDriver.h" #import "SUScheduledUpdateDriver.h" +#import "SUConstants.h" +#import "SULog.h" +#include // UK 2007-04-27 + @interface SUUpdater (Private) -- initForBundle:(NSBundle *)bundle; +- (id)initForBundle:(NSBundle *)bundle; - (void)startUpdateCycle; - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)updateDriver; - (BOOL)automaticallyDownloadsUpdates; @@ -25,7 +29,6 @@ - (void)scheduleNextUpdateCheck; - (void)registerAsObserver; - (void)unregisterAsObserver; - (void)updateDriverDidFinish:(NSNotification *)note; -- initForBundle:(NSBundle *)bundle; - (NSURL *)parameterizedFeedURL; @end @@ -52,7 +55,7 @@ + (SUUpdater *)updaterForBundle:(NSBundle *)bundle } // This is the designated initializer for SUUpdater, important for subclasses -- initForBundle:(NSBundle *)bundle +- (id)initForBundle:(NSBundle *)bundle { self = [super init]; if (bundle == nil) bundle = [NSBundle mainBundle]; @@ -70,10 +73,11 @@ + (SUUpdater *)updaterForBundle:(NSBundle *)bundle host = [[SUHost alloc] initWithBundle:bundle]; [self registerAsObserver]; +#if 0 // Saving-the-developer-from-a-stupid-mistake-check: if (![[[self feedURL] scheme] isEqualToString:@"https"] && ![host publicDSAKey]) NSRunAlertPanel(@"Insecure update error!", @"For security reasons, you need to distribute your appcast over SSL or sign your updates. See Sparkle's documentation for more information.", @"OK", nil, nil); - +#endif // This runs the permission prompt if needed, but never before the app has finished launching because the runloop won't run before that [self performSelector:@selector(startUpdateCycle) withObject:nil afterDelay:0]; } @@ -86,7 +90,7 @@ - (id)init return [self initForBundle:[NSBundle mainBundle]]; } -- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [host bundlePath]]; } +- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [host bundlePath], [host installationPath]]; } - (void)startUpdateCycle { @@ -157,6 +161,7 @@ - (void)scheduleNextUpdateCheck if (checkTimer) { [checkTimer invalidate]; + [checkTimer release]; // UK 2009-03-16 Timer is non-repeating, may have invalidated itself, so we had to retain it. checkTimer = nil; } if (![self automaticallyChecksForUpdates]) return; @@ -174,17 +179,79 @@ - (void)scheduleNextUpdateCheck delayUntilCheck = (updateCheckInterval - intervalSinceCheck); // It hasn't been long enough. else delayUntilCheck = 0; // We're overdue! Run one now. - checkTimer = [NSTimer scheduledTimerWithTimeInterval:delayUntilCheck target:self selector:@selector(checkForUpdatesInBackground) userInfo:nil repeats:NO]; + checkTimer = [[NSTimer scheduledTimerWithTimeInterval:delayUntilCheck target:self selector:@selector(checkForUpdatesInBackground) userInfo:nil repeats:NO] retain]; // UK 2009-03-16 Timer is non-repeating, may have invalidated itself, so we had to retain it. +} + + +-(void) putFeedURLIntoDictionary: (NSMutableDictionary*)theDict // You release this. +{ + [theDict setObject: [self feedURL] forKey: @"feedURL"]; +} + +-(void) checkForUpdatesInBgReachabilityCheckWithDriver: (SUUpdateDriver*)inDriver /* RUNS ON ITS OWN THREAD */ +{ + NS_DURING + // This method *must* be called on its own thread. SCNetworkReachabilityCheckByName + // can block, and it can be waiting a long time on slow networks, and we + // wouldn't want to beachball the main thread for a background operation. + // We could use asynchronous reachability callbacks, but those aren't + // reliable enough and can 'get lost' sometimes, which we don't want. + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SCNetworkConnectionFlags flags = 0; + BOOL isNetworkReachable = YES; + + // Don't perform automatic checks on unconnected laptops or dial-up connections that aren't online: + NSMutableDictionary* theDict = [NSMutableDictionary dictionary]; + [self performSelectorOnMainThread: @selector(putFeedURLIntoDictionary:) withObject: theDict waitUntilDone: YES]; // Get feed URL on main thread, it's not safe to call elsewhere. + if( SCNetworkCheckReachabilityByName( [[[theDict objectForKey: @"feedURL"] host] cStringUsingEncoding: NSUTF8StringEncoding], &flags ) ) + { + BOOL reachable = (flags & kSCNetworkFlagsReachable) == kSCNetworkFlagsReachable; + BOOL automatic = (flags & kSCNetworkFlagsConnectionAutomatic) == kSCNetworkFlagsConnectionAutomatic; + BOOL local = (flags & kSCNetworkFlagsIsLocalAddress) == kSCNetworkFlagsIsLocalAddress; + + //NSLog(@"reachable = %s, automatic = %s, local = %s", (reachable?"YES":"NO"), (automatic?"YES":"NO"), (local?"YES":"NO")); + + if( !(reachable || automatic || local) ) + isNetworkReachable = NO; + } + + if( isNetworkReachable ) + { + [self performSelectorOnMainThread: @selector(checkForUpdatesWithDriver:) withObject: inDriver waitUntilDone: NO]; + } + + [pool release]; + NS_HANDLER + NSLog(@"UNCAUGHT EXCEPTION IN UPDATE CHECK TIMER: %@",[localException reason]); + // Don't propagate the exception beyond here. In Carbon apps that would trash the stack. + NS_ENDHANDLER } + - (void)checkForUpdatesInBackground { - checkTimer = nil; // Timer doesn't repeat, so it's invalid, just needs to be set to nil. + if( [delegate respondsToSelector: @selector(updaterMayCheckForUpdates:)] && ![delegate updaterMayCheckForUpdates: self] ) + { + [self scheduleNextUpdateCheck]; + return; + } + + // Background update checks should only happen if we have a network connection. + // Wouldn't want to annoy users on dial-up by establishing a connection every + // hour or so: + SUUpdateDriver * theUpdateDriver = [[[([self automaticallyDownloadsUpdates] ? [SUAutomaticUpdateDriver class] : [SUScheduledUpdateDriver class]) alloc] initWithUpdater:self] autorelease]; - [self checkForUpdatesWithDriver:[[[([self automaticallyDownloadsUpdates] ? [SUAutomaticUpdateDriver class] : [SUScheduledUpdateDriver class]) alloc] initWithUpdater:self] autorelease]]; + [NSThread detachNewThreadSelector: @selector(checkForUpdatesInBgReachabilityCheckWithDriver:) toTarget: self withObject: theUpdateDriver]; } -- (IBAction)checkForUpdates:sender + +-(BOOL) mayUpdateAndRestart +{ + return( !delegate || ![delegate respondsToSelector: @selector(updaterShouldRelaunchApplication:)] || [delegate updaterShouldRelaunchApplication: self] ); +} + +- (IBAction)checkForUpdates: (id)sender { [self checkForUpdatesWithDriver:[[[SUUserInitiatedUpdateDriver alloc] initWithUpdater:self] autorelease]]; } @@ -197,14 +264,21 @@ - (void)checkForUpdateInformation - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)d { if ([self updateInProgress]) { return; } - if (checkTimer) { [checkTimer invalidate]; checkTimer = nil; } + if (checkTimer) { [checkTimer invalidate]; [checkTimer release]; checkTimer = nil; } // UK 2009-03-16 Timer is non-repeating, may have invalidated itself, so we had to retain it. + + SUClearLog(); + SULog( @"===== %@ =====", [[NSFileManager defaultManager] displayNameAtPath: [[NSBundle mainBundle] bundlePath]] ); [self willChangeValueForKey:@"lastUpdateCheckDate"]; [host setObject:[NSDate date] forUserDefaultsKey:SULastCheckTimeKey]; [self didChangeValueForKey:@"lastUpdateCheckDate"]; driver = [d retain]; - [driver checkForUpdatesAtURL:[self parameterizedFeedURL] host:host]; + NSURL* theFeedURL = [self parameterizedFeedURL]; + if( theFeedURL ) // Use a NIL URL to cancel quietly. + [driver checkForUpdatesAtURL: theFeedURL host:host]; + else + [driver abortUpdate]; } - (void)registerAsObserver @@ -289,14 +363,20 @@ - (void)setFeedURL:(NSURL *)feedURL [host setObject:[feedURL absoluteString] forUserDefaultsKey:SUFeedURLKey]; } -- (NSURL *)feedURL +- (NSURL *)feedURL // *** MUST BE CALLED ON MAIN THREAD *** { // A value in the user defaults overrides one in the Info.plist (so preferences panels can be created wherein users choose between beta / release feeds). NSString *appcastString = [host objectForKey:SUFeedURLKey]; + if( [delegate respondsToSelector: @selector(feedURLStringForUpdater:)] ) + appcastString = [delegate feedURLStringForUpdater: self]; if (!appcastString) // Can't find an appcast string! [NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the SUFeedURL key in either the Info.plist or the user defaults!"]; NSCharacterSet* quoteSet = [NSCharacterSet characterSetWithCharactersInString: @"\"\'"]; // Some feed publishers add quotes; strip 'em. - return [NSURL URLWithString:[appcastString stringByTrimmingCharactersInSet:quoteSet]]; + NSString* castUrlStr = [appcastString stringByTrimmingCharactersInSet:quoteSet]; + if( !castUrlStr || [castUrlStr length] == 0 ) + return nil; + else + return [NSURL URLWithString: castUrlStr]; } - (void)setUserAgentString:(NSString *)userAgent @@ -393,7 +473,7 @@ - (void)dealloc { [self unregisterAsObserver]; [host release]; - if (checkTimer) { [checkTimer invalidate]; } + if (checkTimer) { [checkTimer invalidate]; [checkTimer release]; checkTimer = nil; } // UK 2009-03-16 Timer is non-repeating, may have invalidated itself, so we had to retain it. [super dealloc]; } diff --git a/SUVersionComparisonProtocol.h b/SUVersionComparisonProtocol.h index 3d11ae873..6c65ea45a 100644 --- a/SUVersionComparisonProtocol.h +++ b/SUVersionComparisonProtocol.h @@ -9,6 +9,8 @@ #ifndef SUVERSIONCOMPARISONPROTOCOL_H #define SUVERSIONCOMPARISONPROTOCOL_H +#import + /*! @protocol @abstract Implement this protocol to provide version comparison facilities for Sparkle. @@ -20,7 +22,7 @@ @abstract An abstract method to compare two version strings. @discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent. */ -- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! @end diff --git a/Sparkle.xcodeproj/default.pbxuser b/Sparkle.xcodeproj/default.pbxuser deleted file mode 100644 index 1bddcecd4..000000000 --- a/Sparkle.xcodeproj/default.pbxuser +++ /dev/null @@ -1,245 +0,0 @@ -// !$*UTF8*$! -{ - 0867D690FE84028FC02AAC07 /* Project object */ = { - activeArchitecture = i386; - activeBuildConfigurationName = Debug; - activeExecutable = FAEC981A0D94C8750008EDA9 /* Sparkle Test App */; - activeTarget = 61B5F90109C4CEE200B25A18 /* Sparkle Test App */; - codeSenseManager = FAEC98240D94C8760008EDA9 /* Code sense */; - executables = ( - FAEC981A0D94C8750008EDA9 /* Sparkle Test App */, - FAEC981B0D94C8750008EDA9 /* Relaunch Tool */, - ); - perUserDictionary = { - PBXPerProjectTemplateStateSaveDate = 227854453; - PBXWorkspaceStateSaveDate = 227854453; - }; - sourceControlManager = FAEC98230D94C8760008EDA9 /* Source Control */; - userBuildSettings = { - }; - }; - 61B5F90109C4CEE200B25A18 /* Sparkle Test App */ = { - activeExec = 0; - executables = ( - FAEC981A0D94C8750008EDA9 /* Sparkle Test App */, - ); - }; - 8DC2EF4F0486A6940098B216 /* Sparkle */ = { - activeExec = 0; - }; - D1E42C2E0CE754AE00F50EB9 /* Relaunch Tool */ = { - activeExec = 0; - executables = ( - FAEC981B0D94C8750008EDA9 /* Relaunch Tool */, - ); - }; - FAEC981A0D94C8750008EDA9 /* Sparkle Test App */ = { - isa = PBXExecutable; - activeArgIndices = ( - ); - argumentStrings = ( - ); - autoAttachOnCrash = 1; - breakpointsEnabled = 1; - configStateDict = { - }; - customDataFormattersEnabled = 1; - debuggerPlugin = GDBDebugging; - disassemblyDisplayState = 0; - dylibVariantSuffix = ""; - enableDebugStr = 1; - environmentEntries = ( - { - active = YES; - name = MallocPreScribble; - value = 1; - }, - { - active = YES; - name = MallocScribble; - value = 1; - }, - { - active = YES; - name = MallocGuardEdges; - value = 1; - }, - { - active = YES; - name = MallocCheckHeapStart; - value = 1; - }, - { - active = YES; - name = MallocCheckHeapEach; - value = 100000; - }, - { - active = YES; - name = MallocStackLogging; - value = 1; - }, - { - active = NO; - name = MallocStackLoggingNoCompact; - value = 1; - }, - { - active = YES; - name = NSDebugEnabled; - value = YES; - }, - { - active = YES; - name = NSZombieEnabled; - value = YES; - }, - { - active = NO; - name = NSDeallocateZombies; - value = NO; - }, - { - active = YES; - name = NSAutoreleaseFreedObjectCheckEnabled; - value = YES; - }, - { - active = NO; - name = MALLOC_PROTECT_BEFORE; - value = 1; - }, - { - active = YES; - name = MALLOC_ALTIVEC_SIZE; - value = 1; - }, - { - active = YES; - name = MALLOC_FILL_SPACE; - value = 1; - }, - { - active = NO; - name = CFZombieLevel; - value = 65553; - }, - ); - executableSystemSymbolLevel = 0; - executableUserSymbolLevel = 0; - libgmallocEnabled = 0; - name = "Sparkle Test App"; - sourceDirectories = ( - ); - }; - FAEC981B0D94C8750008EDA9 /* Relaunch Tool */ = { - isa = PBXExecutable; - activeArgIndices = ( - ); - argumentStrings = ( - ); - autoAttachOnCrash = 1; - breakpointsEnabled = 1; - configStateDict = { - }; - customDataFormattersEnabled = 1; - debuggerPlugin = GDBDebugging; - disassemblyDisplayState = 0; - dylibVariantSuffix = ""; - enableDebugStr = 1; - environmentEntries = ( - { - active = YES; - name = MallocPreScribble; - value = 1; - }, - { - active = YES; - name = MallocScribble; - value = 1; - }, - { - active = YES; - name = MallocGuardEdges; - value = 1; - }, - { - active = YES; - name = MallocCheckHeapStart; - value = 1; - }, - { - active = YES; - name = MallocCheckHeapEach; - value = 100000; - }, - { - active = YES; - name = MallocStackLogging; - value = 1; - }, - { - active = NO; - name = MallocStackLoggingNoCompact; - value = 1; - }, - { - active = YES; - name = NSDebugEnabled; - value = YES; - }, - { - active = YES; - name = NSZombieEnabled; - value = YES; - }, - { - active = NO; - name = NSDeallocateZombies; - value = NO; - }, - { - active = YES; - name = NSAutoreleaseFreedObjectCheckEnabled; - value = YES; - }, - { - active = NO; - name = MALLOC_PROTECT_BEFORE; - value = 1; - }, - { - active = YES; - name = MALLOC_ALTIVEC_SIZE; - value = 1; - }, - { - active = YES; - name = MALLOC_FILL_SPACE; - value = 1; - }, - { - active = NO; - name = CFZombieLevel; - value = 65553; - }, - ); - executableSystemSymbolLevel = 0; - executableUserSymbolLevel = 0; - libgmallocEnabled = 0; - name = "Relaunch Tool"; - sourceDirectories = ( - ); - }; - FAEC98230D94C8760008EDA9 /* Source Control */ = { - isa = PBXSourceControlManager; - fallbackIsa = XCSourceControlManager; - isSCMEnabled = 0; - scmConfiguration = { - }; - }; - FAEC98240D94C8760008EDA9 /* Code sense */ = { - isa = PBXCodeSenseManager; - indexTemplatePath = ""; - }; -} diff --git a/Sparkle.xcodeproj/project.pbxproj b/Sparkle.xcodeproj/project.pbxproj index 29559a290..4036b4b84 100644 --- a/Sparkle.xcodeproj/project.pbxproj +++ b/Sparkle.xcodeproj/project.pbxproj @@ -16,6 +16,11 @@ 552B69F010C0791800050E82 /* SUPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */; }; 552B6A3710C0795600050E82 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5F8F609C4CEB300B25A18 /* Security.framework */; }; 552B6A5E10C07FC200050E82 /* SUStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */; }; + 55D8F80910C58B210011E7FA /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55D8F80810C58B210011E7FA /* SULog.m */; }; + 55D8F80A10C58B210011E7FA /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55D8F80810C58B210011E7FA /* SULog.m */; }; + 55D8F80B10C58B210011E7FA /* SULog.h in Headers */ = {isa = PBXBuildFile; fileRef = 55D8F80710C58B210011E7FA /* SULog.h */; }; + 55D8F80C10C58B210011E7FA /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55D8F80810C58B210011E7FA /* SULog.m */; }; + 55D8F80D10C58B210011E7FA /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55D8F80810C58B210011E7FA /* SULog.m */; }; 610134730DD250470049ACDF /* SUUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134710DD250470049ACDF /* SUUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 610134740DD250470049ACDF /* SUUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 610134720DD250470049ACDF /* SUUpdateDriver.m */; }; 6101347B0DD2541A0049ACDF /* SUProbingUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; @@ -154,6 +159,9 @@ /* Begin PBXFileReference section */ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 55D8F80410C589F40011E7FA /* ThreadSafePreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSafePreferences.h; path = Elgato/ThreadSafePreferences.h; sourceTree = ""; }; + 55D8F80710C58B210011E7FA /* SULog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SULog.h; sourceTree = ""; }; + 55D8F80810C58B210011E7FA /* SULog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SULog.m; sourceTree = ""; }; 610134710DD250470049ACDF /* SUUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdateDriver.h; sourceTree = ""; }; 610134720DD250470049ACDF /* SUUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdateDriver.m; sourceTree = ""; }; 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUProbingUpdateDriver.h; sourceTree = ""; }; @@ -374,6 +382,9 @@ 0867D691FE84028FC02AAC07 /* Sparkle */ = { isa = PBXGroup; children = ( + 55D8F80410C589F40011E7FA /* ThreadSafePreferences.h */, + 55D8F80710C58B210011E7FA /* SULog.h */, + 55D8F80810C58B210011E7FA /* SULog.m */, 61F83F6E0DBFE07A006FDD30 /* Update Control */, 61299B3909CB055000B7442F /* Appcast Support */, 618FA6DB0DB485440026945C /* Installation */, @@ -630,6 +641,7 @@ 6102FE5B0E08C7EC00F85D09 /* SUUnarchiver_Private.h in Headers */, 61EF67590E25C5B400F754E0 /* SUHost.h in Headers */, 61CFB3290E385186007A1735 /* Sparkle.pch in Headers */, + 55D8F80B10C58B210011E7FA /* SULog.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -872,6 +884,7 @@ buildActionMask = 2147483647; files = ( 61227A160DB548B800AB99EA /* SUVersionComparisonTest.m in Sources */, + 55D8F80D10C58B210011E7FA /* SULog.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -880,6 +893,7 @@ buildActionMask = 2147483647; files = ( 61B5F93009C4CFDC00B25A18 /* main.m in Sources */, + 55D8F80A10C58B210011E7FA /* SULog.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -916,6 +930,7 @@ 6102FE5C0E08C7EC00F85D09 /* SUUnarchiver_Private.m in Sources */, 61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */, 61EF67560E25B58D00F754E0 /* SUHost.m in Sources */, + 55D8F80C10C58B210011E7FA /* SULog.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -932,6 +947,7 @@ 552B69ED10C0791000050E82 /* SUSystemProfiler.m in Sources */, 552B69F010C0791800050E82 /* SUPackageInstaller.m in Sources */, 552B6A5E10C07FC200050E82 /* SUStandardVersionComparator.m in Sources */, + 55D8F80910C58B210011E7FA /* SULog.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1127,6 +1143,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = FA1941D40D94A70100DD942E /* ConfigRelaunchRelease.xcconfig */; buildSettings = { + GCC_PREFIX_HEADER = ""; PRODUCT_NAME = finish_installation; }; name = "Release (GC dual-mode; 10.5-only)"; @@ -1235,6 +1252,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = FA1941D30D94A70100DD942E /* ConfigRelaunchDebug.xcconfig */; buildSettings = { + GCC_PREFIX_HEADER = ""; PRODUCT_NAME = finish_installation; }; name = Debug; @@ -1243,6 +1261,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = FA1941D40D94A70100DD942E /* ConfigRelaunchRelease.xcconfig */; buildSettings = { + GCC_PREFIX_HEADER = ""; PRODUCT_NAME = finish_installation; }; name = Release; diff --git a/Test Application/English.lproj/MainMenu.nib/classes.nib b/Test Application/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 000000000..141962916 --- /dev/null +++ b/Test Application/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,43 @@ + + + + + IBClasses + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + ACTIONS + + checkForUpdates + id + + CLASS + SUUpdater + LANGUAGE + ObjC + OUTLETS + + delegate + id + + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/Test Application/English.lproj/MainMenu.nib/info.nib b/Test Application/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 000000000..09d036204 --- /dev/null +++ b/Test Application/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,21 @@ + + + + + IBFramework Version + 667 + IBLastKnownRelativeProjectPath + ../../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 2 + 57 + + IBSystem Version + 9D34 + targetFramework + IBCocoaFramework + + diff --git a/da.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/da.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 000000000..09004544e --- /dev/null +++ b/da.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,29 @@ +{ + IBClasses = ( + { + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + { + CLASS = NSObject; + LANGUAGE = ObjC; + }, + { + ACTIONS = { + doNotInstall = id; + installLater = id; + installNow = id; + }; + CLASS = SUAutomaticUpdateAlert; + LANGUAGE = ObjC; + SUPERCLASS = SUWindowController; + }, + { + CLASS = SUWindowController; + LANGUAGE = ObjC; + SUPERCLASS = NSWindowController; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/da.lproj/SUAutomaticUpdateAlert.nib/info.nib b/da.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 000000000..da986de65 --- /dev/null +++ b/da.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBDocumentLocation + 69 10 356 240 0 0 1680 1028 + IBFramework Version + 489.0 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/da.lproj/SUAutomaticUpdateAlert.strings b/da.lproj/SUAutomaticUpdateAlert.strings old mode 100755 new mode 100644 diff --git a/da.lproj/SUUpdateAlert.nib/classes.nib b/da.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 000000000..2a8a8572b --- /dev/null +++ b/da.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,40 @@ +{ + IBClasses = ( + { + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + { + CLASS = NSApplication; + LANGUAGE = ObjC; + SUPERCLASS = NSResponder; + }, + { + CLASS = NSObject; + LANGUAGE = ObjC; + }, + { + ACTIONS = { + installUpdate = id; + remindMeLater = id; + skipThisVersion = id; + }; + CLASS = SUUpdateAlert; + LANGUAGE = ObjC; + OUTLETS = { + delegate = id; + description = NSTextField; + installButton = NSButton; + releaseNotesView = WebView; + }; + SUPERCLASS = SUWindowController; + }, + { + CLASS = SUWindowController; + LANGUAGE = ObjC; + SUPERCLASS = NSWindowController; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/da.lproj/SUUpdateAlert.nib/info.nib b/da.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 000000000..b1ef94ed2 --- /dev/null +++ b/da.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBDocumentLocation + 69 129 356 240 0 0 1920 1178 + IBFramework Version + 489.0 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 5 + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/da.lproj/SUUpdateAlert.nib/keyedobjects.nib b/da.lproj/SUUpdateAlert.nib/keyedobjects.nib index 75b515a5d53d6d70e990534252b2d90d47ccc851..1819d7a8ccac22d26d4b2c82e248a3960624c481 100644 GIT binary patch literal 10365 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 diff --git a/da.lproj/SUUpdateAlert.strings b/da.lproj/SUUpdateAlert.strings old mode 100755 new mode 100644 diff --git a/da.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/da.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..480bb3578 --- /dev/null +++ b/da.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,34 @@ +{ + IBClasses = ( + { + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + { + CLASS = NSObject; + LANGUAGE = ObjC; + }, + { + ACTIONS = { + finishPrompt = id; + toggleMoreInfo = id; + }; + CLASS = SUUpdatePermissionPrompt; + LANGUAGE = ObjC; + OUTLETS = { + delegate = id; + descriptionTextField = NSTextField; + moreInfoButton = NSButton; + moreInfoView = NSView; + }; + SUPERCLASS = SUWindowController; + }, + { + CLASS = SUWindowController; + LANGUAGE = ObjC; + SUPERCLASS = NSWindowController; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/da.lproj/SUUpdatePermissionPrompt.nib/data.dependency b/da.lproj/SUUpdatePermissionPrompt.nib/data.dependency new file mode 100644 index 000000000..b7381f72e --- /dev/null +++ b/da.lproj/SUUpdatePermissionPrompt.nib/data.dependency @@ -0,0 +1,10 @@ + + + + + IBPaletteDependency + + Controllers + + + diff --git a/da.lproj/SUUpdatePermissionPrompt.nib/info.nib b/da.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..7b0263276 --- /dev/null +++ b/da.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 489.0 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBSystem Version + 9F33 + targetFramework + IBCocoaFramework + + diff --git a/da.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/da.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib index 608faf4b10570185c884d2821940b96cb03844ed..4ef0a8b9be7a9a6d434ed0d3f53fc5096f24b9f0 100644 GIT binary patch literal 12807 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 literal 15232 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
  • }lK*YK8MpW%Svkl~o&xZ#B18^d>olPTJi?3DbJUMVFh(^6_vyeai5 zz7&5-Af+iKlyYmzlPT*{o=bT?WlPGoldgBJ;M&l;qX5)6_ z4&zSaF5`#BFN{AJe>DD{M$?RG8EMWmcUoau&$KaVQ`3BDGt#b2yDsheG?sQ_+WfSe z(*Bk9c-p$O^=TW@Hl}S#+mf~|ZF}0gressLDaX{+)YH`0bh)X&sl-%f8fY49sxS>T zHJGk8-E8`o=~mP2rhl6jneH|%Hmx;%YWm#twdu6!tm!w?AEt9=WF}^6jx#5i&1Q?a zm$|pOulaIwe{-q1+&suU)f_e7WWLS3(0qq^k@;@(V)K3GW9F0QQ|8m=Kg{RTQ_`jM z!t`$GJ<^NQ`=s|vzao8H`km=Z)0d?`p1wBy+4SeqpHJVCzAb%w`i}IS>ATYRq<@xv z%7QG!LM?HYBukpbYUyU_Wht=?w~VlivP`hlT3RgETUJ`uTDDrYS+-kVw7g{bk7bYL z70YXuH!KG&?^-^!d~W&Da>DYBpNYAijazS)?b5@--&)UUWV3n*z*6!Ax)^XN=b*}Y(>oeB3tjBFCTbwPyrm<;li8h@r*(Tb| zHj6FC=CtM7@@!pf#kO+WDBC2P$5w0e+pf3Gvn{qQwQaC%v%PKm%yud}EjuH-U-tOy ziP@90YqCArwb|b6Te8<=@57aOnf-S52ieE%*e=*r_ISJ6uC?pz$@V<^ z751U_;r7w?YWrCGMEhiWjXi9?-M-Mi*1q1p!M@SH$-ddX)&7G0HTxU(x9soO57-ac zf3%;m|7<^N|IPk~{alVYr)$oYIm2=)b4KQj&Z*8Bn=?OWQO+|tTXSB>`90^)obwLs z5F9E;yhH8KI&_X?hu)FmNOhz+%npkq!;$5%IqVLH!{zAg$ai#gxE+O#ZjK&~Vn?}S zqGOU{iesw7=a}JWa)cZa$IXt19IG5V94|XQc6{&n#qq1-cczzL53}>S==)A@mb~Za_Ia{1_oO7KwIHS&a&IQh!owqn|b1rn=;audr+qu|zpYs9d zL(YetYn+ccPdLAEe&;;tJmoy?{K@%?^H=BZ&Oe>!UDzeKRIYfJ+NE{rT*)rIE5()S zN^_ZA7FUKV%Vl%fT@IJaCAo@RVOO(jmaD}z$2He=gDdKq=UU*p*>#KSHrGPe9j-;L zyIqT2_qiT$J>+`W^@wY^Yo%+o>rvO^t|whjyVkj$aqZ8&Ja=|pVP3zyioA(=x9081 zdq3}FeoDSG|H}N9{0H;b3S(D&6jSLZjtVi?vd`5?w1~vmPkvb zWzq_1m9$2BOnO3kN?I$emo`WnrA^XiX{+>t^rG~V^dD)r^s@A-^t!ZHdP{mo+AkfH z-jxnZN2H_DG3mJUiS(KDh4htlN;)l_k^Yp<7otLSp|&uwFtsqJuybMG!U2VqNd6)G RP#Jzyt=F`bZwM8R{C^$=gt`C# diff --git a/da.lproj/Sparkle.strings b/da.lproj/Sparkle.strings old mode 100755 new mode 100644 diff --git a/de.lproj/SUAutomaticUpdateAlert.strings b/de.lproj/SUAutomaticUpdateAlert.strings index 94183db3558c2c72f3860b45bb40f49c9c88cbea..276eac86431e8a8f46cee316f9ef1347e394c2bc 100644 GIT binary patch literal 1326 zcmchXPfx-?5XIk_PqE=5axoeb6a15aT)1G=o3{!mm0C=J7{9#wn<*7a1Wb%%v+Z_w zcIM4{GyVLi=ty-1T`5tdDI@5DSyfMWYHO|;W1KKSTHL?ZgPu9*bEB)yx0i;F7Z!DpRapPt@6SkLqGZgJ7>2EZOx*@hPn6tT=jguX0ta~dsXp>8<7b4lp@*4Ovu)C_$lAUti z>*@#9)N;Nic#S|Fqg}4`J5dJI$Qzy$-jQ=$=wFU!8oY9D9)@0Rc5d}F;ydWkW6WK# zL%y5LbB~r+TXXLyEb5bRC-k&Xv;bra`#4wpFX%+soqIA$b zHe+TAw^r@eiPq7Dic{~(;QVK9XUb$KX*!nsrmW?r+WGsxqyxTG2adPPYWZK;)h~^; Se%a=!V6x}6Jo0GUGW-Is7Tx^- literal 1074 zcmchW%}T>i6ohBpr?|M%Zp4KO{y|+!7Iq=HD@|%cYa;nUd|#hK{m!|Cm|CK^C?Rd; z+;iv7nMpoAb*!<15>54>T!m7VdZluxBYrQquhg*8Xsrx<<7*|A6m;rcV^~Hy;0n6t zwx>%fqi^iDYT;;LFPvfGxYV;|&OhgW&Gp7=)KGv*swtJ3D+{i$ra$%uI9^ychD|6H+REz- z`YjhrwM((q9PfksANN}_6-io-k;zysQ)BG1j3-*h@9)k%OvY$T@w_`wJZX2%Lp!!J zIqPLIw6pVX%6s^}Q4mm-p)G8ttGxcdxf1^x|2k9b-kzubjsBb`?&@YqhSYxnG61xt diff --git a/de.lproj/SUUpdateAlert.strings b/de.lproj/SUUpdateAlert.strings index 0ef30484b27891f7f1d0a35228f9350c84a1008d..b99899947f35d1bd5497de68bc863c39ff299ae0 100644 GIT binary patch delta 17 YcmbQlJBgR+|G&xhOi~+VOW7D106HlJb^rhX delta 15 WcmbQlJBgR+-~Y+BOwt==O4$G}Fa>h} diff --git a/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..0f776c895 --- /dev/null +++ b/de.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/de.lproj/SUUpdatePermissionPrompt.nib/info.nib b/de.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/de.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/de.lproj/SUUpdatePermissionPrompt.strings b/de.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..d3fca25bd49af02cd8f7daecefe1b65f54523a6a GIT binary patch literal 3058 zcmeH}TW`}q5QXQtzrx}LsX_(Br70CcZPH4WsuZbtLVYnIB_WAj9EXtpb_9H9H=8)7 z!H$Z=11NI5m)Y65oHOGe-*@b>?OJ3_8(C}smmLF$CwG02JU`KU3X0Dnt zr$VHxssl`zO&qSq%x=g<3hD-~(pNF5VNXhNxc`kIQbz?_S2zjyzbsf?v^lbGbRQuq z=ZP1Xs#ABi$57G2I?;@XYO-K-sL;xW)|Cn6qerIHYg7JJ&qid#?681Bs~%a=#7Z0e9`T()9O((XXP{N zsy^y0Ixm}JqAhu8$vGoW_UwvXJ)Ol$@K6%Ee^VT^uNvoVpJKD3p?nIb-mh1c>}=Sx z5-R^^>ThK#u(?HEJziE7Xr0v&-=M2Xw9aXD2QfcGM-IbtyLBkhy0i)*1KwhSy5%j1 z93m&S39X^CoH#u*R=Vt~E~@K@AIGfei%UowF4>_@<@=McCkr(bUv%#yMlM+-W4|i}0D$8OX8-^I diff --git a/en.lproj/SUUpdatePermissionPrompt.strings b/en.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..75cd357e7f3d1e8f2fbad4c9c4e854988361c32b GIT binary patch literal 3038 zcmeHJO;5r=5PfHUMdQVUgCFq&lo*4c2?+rcITZN=gbN|< zc6VoHXWpBc<@+mxLuBEggg#tM_;YZ@XAd)BM{$FI*!l1mG3~8a)pK@+-*}M=>}JJ&t4M7&)9rqDpJm@|9LTnZs40FB^>Z8LhI=A~DO@ z|Dw%BqK&g-ql?>5j>hJ3`nvj!!$F7fjTu{j5i3%xwoTo-u}oj5;5r|Pe*F_IPU~CR z1fpfsiD8ZJ2FEJL5^JT(?(~S;6nI@k@Jh00(MXa@j&+qQ{ZTFxwFyx|t>4G4&mgA2BID-;JzCnE}6srsI9k${0NyQ0Crn zsC!)+E$&FhHMg*}-=$rL8K7*?>8`Ol%akcs-ng#*qPs)-^4aBWE_roWpE`=<08iZ2 z(+F2=gp$ztaX4sSUCe=e59qVdXc`@PE}t(GD>A?EPh!W^wwSUNyklTn45;({04f&p A`2YX_ literal 0 HcmV?d00001 diff --git a/es.lproj/SUAutomaticUpdateAlert.strings b/es.lproj/SUAutomaticUpdateAlert.strings new file mode 100644 index 0000000000000000000000000000000000000000..e1615a2f7c782448632edabaa5d7aefff997500f GIT binary patch literal 1262 zcmchX-A=+l5QWb*PqE=flHKiD!8hu+|MEzz;4O9>lBWbqV z-T6OrrtcrBX-`cBT_{zgDNoP|tFE4Ibgo=8p0P4CVsug@N62SNSSfdoQHJW5rG zG1R{KXp48@=;kTC2D9M|J;umOjvxAoOqM*_XEJ)z>|I;(vSYUs@SKGp~u z>$Krxy*C^O;P^}>DSPA{E<2rYyU2iCkI2nK?7T9$F=xm*=+0y0CKTen-RJh zC$lJK+?a23m$MrqUj3Y4mwBXRe*FE<&~eW@L=2T_-ZTTP$_)JEsYI(swC~su=@T>~ B(E|Vg literal 0 HcmV?d00001 diff --git a/es.lproj/SUUpdateAlert.strings b/es.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000000000000000000000000000000000..7d1f2ccfe018259c6c0c1bab56e606dc1e64222a GIT binary patch literal 1668 zcmcgs+fKqj6r5+iV&fw(2Cs?62b7qQcu55Hxk44R6w-^Rf6F)cuFmXILMpU1A*O+L zPrK*L%$c*_Uo~u_4udlU@DTGdIN@&#SLom#LxkvKj430>j3X$J-MM}I0QHPbdj+kA3Fu1gQFIZV!jw-O-S;4j_U&dFe^oTHG z-5LK!TnAiN>F8hhb?`!V6LRZfz$fW@XSqpJDdZ+#otM?o-5kd%IWwN>tFijV)Kt#a zJQ*Tz>wJed&A&~CS>=Frt530*@8y^mXUjM*f92R}k`ub}_hfIHZ_|2zOh3uPL)KNz z0Pp1PPQ1|i%(H&?=DwHzmsLua&N?|p?{!KJ*f+y-QS+6uk7XyJG(5;@`2PsAzO=0C iq)3D(`!64^a_8!PX + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/es.lproj/SUUpdatePermissionPrompt.nib/info.nib b/es.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/es.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/es.lproj/SUUpdatePermissionPrompt.strings b/es.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..60c1329adfb2b7ff61108ff1070569196ee613f1 GIT binary patch literal 3172 zcmeHJO>fgc5S?>>g~bJ_LIuP}3l%~oDN>~u)=9S9T?mmZZ{`89tFb!}peS@1VyYEl*`2mzWvC{zTeK`-k$V^0f;jJNUoKgJW8?c8Fd7 zLKH8g1b2mGG6}f-ISVbhP_D1Q^O5e$WQ_dMa)^-`TvWWT%glY*m&ZYFHudZIY60z7 zrjlbE;M^Q|`b#{Ymw3iNJHb<|=QZVX8z$Py*KPP_MLH{1y}yclKZtytACI()b0TmX z#>7x#l=l71un6d4Ik^<`07SZ+@aP^$B?hS&D!-K@1nb zzrfhnK2c&!G5=AY6YQFxLfE0|*jCk$#rOMJZ_6pp?&7zHv4gP%b#>6sUd!Hh*P|^tjDTiRDck!tbHI7>&n(1< zhc__$4*Ob)JlF~|!rPqoBiI)B=CCjZ=4zY4#=CAL@8KnvE7^n1E6pO`1-OG9jh)#P z>HZV=jZg!u2U9J*TCGsO?=VlL^1k7I)9gU)*@+7vW+Z#^2CMw`su_=v7WBA|2d=ZP z8TJyG$;LqU5IHeF-Jlj91hx1-Q_SMu$dqfigY(0@4^aoysB3t8U9)Ggo~Ru9pW-*y zf4p(n>-fx0XSG^uDAtt-yV*ku-NkpAOV~Odc1rrq@yTG9>RPS6tWuWU)FW8VVUf!n zMZ(D5V$Kk + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib b/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/fr.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/fr.lproj/SUUpdatePermissionPrompt.strings b/fr.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..0679dba4d2fe62dacec7603d090641ba7f01f24e GIT binary patch literal 3144 zcmeHJU2oGs5S-`!iW4tLm0Cc2v``@w5|AoIDT0Ux#Ea{=&^Bp|?EvA|@(6zdnAyw4 z;YjL41@Qo?Qu};+dpol`d-wg@7H(i04s8sPpyZ#!L;kLzhZpE0!$(wjic`J~F`%8E zL(U)2s^a&Gqint^tdPe=%Of1)j9*gPAEGn2zk|CoU)zjii|@-kgqW7i9dOmZfZ{PG z5TVPnYo2M+>3IvuHTTam-vc;d!AVR z6pSW`qIt)0#@I5Vs%*EWY;M9s$Jn|~&#K43iLc$uaPLL9SNRDzdyL;9Hw8Jok5FsB zCRCxa*H>DN$C2MLd3NM7r$&|M%#<9nUFZJM8rk2ESh)I*fv@L`FSUFW^Ji0SIO{X& zPa{@SIYaoF<1?b47=DlH_jBFB5%=lwf1l4TpKa=@%TD$RPu5`ae#B&xzra(IU2|P+ zqLxEyFeS?q){yEs;aJ^fO2);oxGy;>t-eMaed<1q^o`0jCi>Ej)AEbRi~QL znN(Qh<*ZIhR*O*lj7Ru9N@3INB9g z;Y?pc;_@>>s54$zPiR~5S6H6KgV-B#Rc?6Iiyc02T;LqLe1B%@(+xq)rN&uBZ#vI| zfAx9C`cSQC_r$z<-lzQHd%dd1XGAe4ycft`UMj8;l3l!Jl=}5&UxZxNve5pkc+kAM zpO1aQn8n7itx@E{^X{*NzntI$J{D zTH$M7=StAa;{8Q^PP^3#(rCaFzgAFAn{cC1HS6vnod&&R_=KH>opXyp=eKk#EvUw0 zwKyoI)aFWB={8+0Xr;~gcMO4U@d1@?UTnr_xnmbv8~Dh6?;AN*ls8q z|A^igbGik!@ly~e#59fBP32dj4AK1s z94WgGT2`7SoUUqJ=n1<;7=;cpyYLigsSbITf@(l~8p= z#~p{ds*c+=Crzp;XU=-v>5H$NFH_RPU8zmk%j^7GhueJFoUZCPh3_nA;xtCZB>0Va wPw<>!^Z1uO^HGd*ALko$W}YkcpF)?53CE%QUCw| literal 0 HcmV?d00001 diff --git a/it.lproj/SUUpdateAlert.strings b/it.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000000000000000000000000000000000..07e4c36cb478b782c239daacdb76eb157602ad03 GIT binary patch literal 1658 zcmcgsO;5r=5PfHU#l|BS<9DL*fEp7L#RO2#D^#eK1q+D&bT8_gX%|a_ZB2-2pxfD< zkN4*7e0|oijXDg@5y4{0$KaH|Ews?VJwiD2F~)+H6WV9A%y>3fp6hV;k+uc>W3}I6 zLLW<8kb2$V0Z;tu(bmMp()S?Uud|98*JZJMZ4C_ggv=ac#JG-e$BdfymzryQiZFKR z|3?2y#tVq7PcGyg{ffA)`+@Q$JhYfwuugGYf_Dc;*vVloj^+{j9ESCU%o+1P=~z#B zNHUjUJ;R)p3RkWY$`NBr{|&B*&)_QUYFq*5?i!p$@jTKtbM~~eq9N&Sq`jTc{=ph% zw8qLpmG1rpaR<-LF=37_hFr;AxMTj9E76*(fO;>7x=Kusyh%+vMQ)k-g}o9@30{NO zl{%7pwaIQbnK{WN$~UNfhegx-In>3`e(+{QBBaIvrNo0(@Yd0`=Y`jJ7PHbfT fM)9q(Ay&JFRX>A;kpVfC31u@g+VRVu36+Cyz#0fk literal 0 HcmV?d00001 diff --git a/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..0f776c895 --- /dev/null +++ b/it.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/it.lproj/SUUpdatePermissionPrompt.nib/info.nib b/it.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/it.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/it.lproj/SUUpdatePermissionPrompt.strings b/it.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..321d07adfb841f0bf9347b7827c1469b91875fae GIT binary patch literal 3120 zcmeHJU2oGs5S-`!3X7LYeJCJ4TBr~z2}qTq6hxj7FK+4t(_Wb>35BG6^2rY~dBj*+21%LO@#e4LS;2R3OLCU)kE@`K6#P=t( zDmY*8DY;$cE97zA@*EfV%$Yvz&(OZLe~c%kuLDN1$NNnlJf=s7F>eumVS+oUa9JOb>C7v;( zoiMt@_N&8lmrArPf_?fHBfTnty}!%+sFL|MKb~pV_K8?s;j^CctYKEp?dDorl&YN*(L`d6nwy>U-ZVS~(+g`>gSt3{ppr z`7AasS>cScIva96<;JctpQfhl0kO~-P^YS6JgC&+?mK#=s-D&(6SppnIXVA5di5gD zFtrR7|DWg1>5f&57~`bWv-fPFpm`x691`ZE(^r|Pr;Uh2cq2^IXJtPy?azsk&=jT% zv(0GZnu+k$baaJ7+PpO!^&OxU-pgX%sec9rr1Tq+1!9DDkIieS<&raBIIrHLvqo_% z=9s6y?B%848f9{bk9@0NublA=Wug7I)j{vo;X>?l#;j^w*cwHy{`nl^t{JTJ#JSJt1u{i` zRWi#_87$Xuug6T6bj#5QH_h6$e68GFK7VgEk7sy<7kCWKnnrAGCeEZmV?X24e6umH z<>v3=>W;)!&yJ15`agt8B%6xDW@_`5^TYp`j5Drj#uQ4CHK)AgOl5j?x$JF%&7Mfq zTR+j_V|iX8%kS_SU-1Ujb=HS)2g5jn)@?#ADQRNm@au9+aZE7lQ|vPrmUSC4BedUooNF&Av$6xOYUiHeY{q2E6lX=|P{3T8Fde#4RrClV zIBfkyi&SrBm+RvRp5hhWGebUO84vIfpO_^F+_`Pai@r@F6eCi8t@doURD^Y1(N&Cd zL|^Z{fM4x51){vRqj>8bs`BHePDA3aOetTpMmC_#6#snFv2M`HwB)6eGUp6OdIPIQ zmOCornfub(pP=0s8CJISbkA5_%Y-slu5(@cgm!t=SD!uJyDG08vP2z?q*E?)S6{cb zVk1-uJ-;3fy06_RChB`gpB0TUqobay&&L~9T#p=zWZHkG{uWb>EA-`P*M$HrcWZ>hAlgHBH z5cgcHRMLYf^rM0zExXva;N5O_2C3Bwo0}h4tZFy=4kjq> Axc~qF literal 0 HcmV?d00001 diff --git a/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..5220a221f --- /dev/null +++ b/nl.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib b/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..d2586ea20 --- /dev/null +++ b/nl.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,16 @@ + + + + + IBFramework Version + 629 + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/pt.lproj/SUAutomaticUpdateAlert.nib/classes.nib b/pt.lproj/SUAutomaticUpdateAlert.nib/classes.nib new file mode 100644 index 000000000..46e6ac4c3 --- /dev/null +++ b/pt.lproj/SUAutomaticUpdateAlert.nib/classes.nib @@ -0,0 +1,50 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + doNotInstall + id + installLater + id + installNow + id + + CLASS + SUAutomaticUpdateAlert + LANGUAGE + ObjC + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/pt.lproj/SUAutomaticUpdateAlert.nib/info.nib b/pt.lproj/SUAutomaticUpdateAlert.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/pt.lproj/SUAutomaticUpdateAlert.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/pt.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib b/pt.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..2fb25911f90c0ec056df118e8103dec5153a7930 GIT binary patch literal 7272 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 (oid:17) */ +"Automatically download and install updates in the future" = "Descarregar e instalar atualizações automaticamente no futuro"; + +/* Class = "NSButtonCell"; title = "Don't Install"; ObjectID = "44"; */ +"Don't Install" = "Don't Install"; \ No newline at end of file diff --git a/pt.lproj/SUUpdateAlert.nib/classes.nib b/pt.lproj/SUUpdateAlert.nib/classes.nib new file mode 100644 index 000000000..753be8d36 --- /dev/null +++ b/pt.lproj/SUUpdateAlert.nib/classes.nib @@ -0,0 +1,69 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + ACTIONS + + installUpdate + id + remindMeLater + id + skipThisVersion + id + + CLASS + SUUpdateAlert + LANGUAGE + ObjC + OUTLETS + + delegate + id + description + NSTextField + installButton + NSButton + releaseNotesView + WebView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/pt.lproj/SUUpdateAlert.nib/info.nib b/pt.lproj/SUUpdateAlert.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/pt.lproj/SUUpdateAlert.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/pt.lproj/SUUpdateAlert.nib/keyedobjects.nib b/pt.lproj/SUUpdateAlert.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..7813b4bef84eb99479d4d10d054e403120809211 GIT binary patch literal 10678 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 literal 0 HcmV?d00001 diff --git a/pt.lproj/SUUpdateAlert.strings b/pt.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000000000000000000000000000000000..90520864c788fdf8347866a872c8b385a09e674f GIT binary patch literal 1676 zcmcgtO;5r=5PfHU#l|CvG5DEiJfLVo0-6Zwxk`a*VM$wp`gc9}3%sgtrY#>0v^6oN zf$q-k&YSmUcKZEQ!49f0IEN1hAs>TN{qR23@X2G^<;fR%y`)E%etT3z-u_13H@fZ;J>g*{a)z6G1=Su(qq;atXN%DagHvtr3k z7#duVZ^ZFxp8uM4lbA7%)Vo3X`zw6+Gko*CEtoDaW3KD(^j~wWYxP^!lvJ_DjwD^g z$18cqeQwN|rL{+kqutMN-y&L3Xu8zlnp7cX&*z_>u}Y{AS25QOT+TKg<#iJOBUy literal 0 HcmV?d00001 diff --git a/pt.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/pt.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..0f776c895 --- /dev/null +++ b/pt.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/pt.lproj/SUUpdatePermissionPrompt.nib/info.nib b/pt.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..6da5b4705 --- /dev/null +++ b/pt.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 677 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/pt.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib b/pt.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..a589608234dfa621f7dbeb2402e01ead2e551cf6 GIT binary patch literal 12573 zcmdUVd3;mF`u@zElQc_{o}^2{JMbz)#zvk2AY%}vN&-={# z&YY?SpEnfF$~uHFB8WpgQXw_cBHj27vpm6&H{dVp5Dqj<=->`k*LmU6_zr=p>7MFv z$c*rUXZE8Qq?ueeI8(V;;|{wit`y>mN=JB_JT-ZdM(a&D0qIdJ%0|6WA9NGC8Rem& zXdJ3UJ~R_8K=+}?&{OEY=r!~f+JSbX_t2;4YxE8J3H^+ILsu}yTCBr*Y{UsT1KV&1 z+!1%e-Ek()!aZ?+JO~fNBX9{Gjmz*jJRVQRQ?MJ?;08Ps2XQ0*C!UWfz8&9#@5K+{ zrT8)YIDQJfgLdJkaVuVdSL6TSP55QJ6~BSs#M|*Z_Po+zTm#(&T{9tueh(dZ@6!{@3`-|AGx1-E1%B~{BXXI zzm*@skK{-3MSL+|!jI<1@TGhiKb9}&$MNIwF@6F+k)Om@@RRu|d?o&fpUS)WDy-(K z`5NBC*YPuW|JV-xMxXB!!Hf*Zh~kidOvsGlQ36UtNyvhdQ3^^$X(%08Q3kRh5!sOg zIZ+4H5xJ0rI-$;}3+jrxq3$RX^*~wWMWtf`fpB?;YM(n4np{*`tN<7g8j_(pgkAT9 z%H6(34_rwpD$T2|_Jl&-DzDEQZYuWHMD9V`ApyT1&<}ADnp9L;K$+3kD1LOM-#=hVTK@7v-XU zsQ-Ay=$_C-<-iYZTTy>BUj|PBpqZ-3*uu-WeJhrG5zLk~*Xc+CWUWh}(Q6ah&jX)#OC{%=s zQ3)E2#-LJEhQ^|Ds-;DAES*d}w3gP>$LI>Wfxb*%rLWQLbjKl(VLY0ECZb8G0zfwf zJe2PVg}r_^!vct38fXkwd&U)&j&xUfd~_7mQ9?%^K~tfn8&#odFk%hBavG{db;ygR zqZu;zm}|y(LJa|bjVA~imNr(+@_J^6pvzeWK6h;!v`W2mJ>Y_&bHW}!vottq1l60c zjt-}V^wy}Ze%hlI1yBQxrFkb{^dO8MLSc}h5u})fW}`W%33Ql;{(+j&Khbx!_tAhcb&l3byWEX%SAz2VGuyQZ2KFp*lZEWxanXGVJ9&N=i4jKXO%Ha;2 z0jNRXFgpX2+)M}2fhJ<6hGIcLk-t2O5c@X)5OuU4HBw_#o4aX`6(HE{XdYbcM{oW} zwr2?4FWYn>S_B%+0d*InCFnu)5L$|sp@-po8Gyc8u^$}e2ddfla5Q+>kbo}`1mon= zUII_!Oj<+*+8Zti(J`N(J(i5>t!M>WiB_R!WnZpF&!IIi_P^0u z5N{oN9<4_k&_?tE+5~dEh+aaQ(H8VF+6s4HMcXO`8%Pm!*VcOdwF**3xkEDw0>Pqy ze`KKA?E~4$6nQHJ6UgbVo>3bNH2P~~c`F4o1h>xzkzdx-008#^2Z29^1cH7~km*(_ zK=>DesR4vOUulE88b%uDtqIr3ga*Ad*8B4s!-1eD}@Fq2V4k z#PtyH3B;4^4~X%6NR!wlcwBxM+`-@gcSd>r(T7I4=g5a3m~2xfq?UH27U!U;idCpn z;GBcZ53V?)ak4Fg8PlT8v3 z&kcc~46En^Ic#tUXBmWMzU2y{4w_DrX$s9KVVw9a`VM`M&Z8gDkLUtel93eruqgs~ zW12h)O>+)%MI=r&#imz`Y^?UyxQ7MZKzWLHenG#sqF>PfXv*fjgV6Ku=pwoVcD*da z9AcJXn(^znI&auRts}BBd$PZre*M5BY`VFMQ7cAh06Zhh`yn#I>F=0e4qd`3ti~}| z!_;RCz$9es1fK8(YUL@-O6`K0iquwK+7t?V>Vcl?Wbd_Epq*UUK&^wbl#y{97FJ;a z1ApOoG(Zku*=5(9FH=41r@-s+)i4KuT6uCF!A?ML_H*=_<8UH#9Ztd)oQzYzb<1!% zwnq3A2%4e6%-gSE+J$zc9l#;LRs`az(*!n*$hy1E0B8dkz=1W?N+s%|wn`yB3=jT+ zXxeKifri}I4)d)3-PUTh7!pq@`6Ek6QD}| zz1|+9z^FyCJ#uI^wN0pv5@mZsmQrheR%Y)ASjM8mtI%Owu5b~=9ScFQpiE!cz*&sE z+UTzN`tc^hc$4Ju`qDnk{z(!0|BWA~%E8h{`HKV#uENzTakU}?qXD0{fkEmRnul08hs=(01&@^>7@3$S!jSYdv8&P%sQyKnhq5^E^zXvGgX|UnY7A zMI{|TW1R!fD)C<_B+i@HJ%=%~Owml;oZijN6_quCXa><=aYP7*SK)BfOdw+P{$GpO zgy({gOvL7h7`K=(Rs{HN<3$laxDM zO8>dt#J*Y0%{OB7K8X2+coD>W173oyW*!6WcA-OQK16w*Ss28kyi#~2&mZtN)dw0w zVo2sIu^||k=Jk0*uYXz~ST850qBkTqhCDT5I3U(}d<}3SHu&6raav=zG3XI%JhME$ zKtnxvPGq%9rdZaGjg=-RP?kt2|-|M(TefrXPu5x`5g$B8Zl0S~30;_{mlH$tV^g zbCS%YkQnpI>5ksyn$f=ucIY^fzfn~ zmX>@d=PpV$&=tRgHv?R^K>qR&4bcZ7mgmjO&H@1DDin|n2<(;f*9h5$&fzr-Au>2h z0UX5)7jgN;Vo`CKII?(1-pImi%=>=Nx25P+3a%wxv%~Gbp zjp1;>FQ*nc^1KSg#?YCKUbqY3lCMNt<$HYYCYW*BHK7w}vXCO&2}w*coj}WlU1klG zKr&ywym!#mc3BNRjef*uSo+dMp)Zc5M@I|O^%iOg9 ztx2?!P6by8u<8c*$?45#D@P{RKK;6hJpn%-w{yppKRDz9z8Z1KMSPNQga@ab$DfcG zqQMvCb%!>$6xPF9glwie7$Jcf!F*NJ4c}A1gh(;rg4+gN1$Pdq^Hk3eS-rt{2Z;U$GNIOWpY@~8J4LltV)Ykeuqau;r z|2lh;gHWK+O>wW&@;|4Nv}!4WpoD7IYB_zLOOT zMr`DxGr&f5lkx+82@*4AnbB!&qM}J86WK@>gr6Ur|L6&lgN&pn=|y^zKBOOOHR!74?nse!$wERYh+)3P|id468cq5%OMH&tGQ8u z-DEbI)26^AI*D^NnzAuA2xFEt4q**5JKQe{P6NKVpWGHC?6yA=`kx78_ z1>`QkQxQuzd@@DIsQ)Lu4fvrM=A^k$mQZ9Ke$!~+1+may?Q5)IdBmURE-Y=%A@`7b z$$jL0r976`)#L)Lh0doG@^E<==J46xaCM!6jm2b1D_O#j0c)kotPiEN+?FBYA+nS# z1Bl8^<<9P)w<|3lA&<6_M_EglgcM$upHh}-Alqc=FnJuulP8oE4+8Ni@-*5)mXlV+ zx{#d?t?`Cg$q!?&bsn}#6M`^ULhskog>(Vf9mHVEm}E7Ld6qm!){uXbwd6l!9leX* zP5(vjq4(1JAU(}%Xn+*1B)>pxa6=VZ6>tY3dxP$--76Y)&8!_uaH;KI|8;1vg$kvg0{Ju>>|5Yk=@a`I(naR$HaMU1f_Jd7rnDW$t~sC7BU}Ysi|KM9A;RgM#$Pz4;gycQ$AZ> z>OTsw8BabWhuP{gIYN$-V=!GDCnxX@@-g{@3K zc~*_wXq#2bmCZfc3Ukhr^a)7)9;Z)L3f4MrO^wIjUkuf;j6E+jBah|yFlDpKqFlKP zrOcn+29J=_=oa!na)x{f4$PbZyAO2va`FXzdO0~up8=!6EWx%}%3$#)#HtjJyb>ex zd6B0U)E*oNKm`(U+c)IfR`RX9wx92nS4-T%CdC!slk+Rc`Syv!A5}bZo#nu8zF{ zM7mbCK4&49TFE6eKsig{Qde@RTpE|okPOS$@=6KI;~|lQtpqufSH!+{5%P{}CzXN| z()UJBf6)z#E&kdzEi>CoGgf{EApLg_{b|5(PVzIE#JM<0c0Hjlfa~YdjdT+%fk#GG zFkR~LGj}n#gDN*%Yr{C#jq5>|w{qRNO!^{ysg=v(vgu|DSONWuU8?OCXzqfKD$OUdxq~AB`8xT9MR|;K047Rr; zdmAW2UWJI*j#b)`H3<2(T-8UP1)llahyT=tjPoFI``b%|?IjnWa9>9zK^C|RoU5aIV7)3+&PVg86ZWIZp%&-{f~^Dx zme9GZci4jES}0F)4O5mddTg1xO1h5Xf z>yIQ0(HP&+Wi(7;ca;YZYTE+w~O1&?cv_# z_Hz5U_qg}D54io@0q!7oi2IN`%pKv5a>uxj=s|jjen=0~BlIXeMn9s*=?Qw0eoQ~1 zpVH6hDf&4*P5(#F&@U>5%+fHckVUpIBX&~i=D#ioI!|in`-*7c%W@S++vK!WWG7;$ z>p;Ttpw7Sp%x7ZMY2r1n0sIFCfzmD38E$DD2=)X@Al^8vHjLuI!ELA2m1aq(c_=m9skPWq=OuZ<&6K}O^dM*k0oe?!h`Vk zMJc><(H5Dg4Bp8oim>7+;DSQNjA#twMpP`b;|`f0J@5|39(W^Ta)c+x!h0DTfCr*+ z`x?BxQ6FK=THuNznK@quo|sD?i%81bWa)U3xAP9($#>v8@-ANDJMo?QE__$M8{eJJ znO>n+TQF+DxCO)8He3tlTd=AHt6OkP3)Zw?Z41`5 zV0{aYZNY{XY;3`Cf+(-C!8ZA2;d!B>APKh%i-c@po$!FrN$4&Zggb=g!b8GR!6;-3 zU4_oVeBqzMFhM1B6BY|ygk-@k+$K1Ldu6pz0bfgdM4iRM^wf4-FCW9kDIee)TKNWf z$E$F#lBTg8YNUZTtsL-1RW`h^GZ0>}83X>BMwXCAVdj@)kaYPvbMd6ArL@U$E~0ctNZZ zUjGX5bNIXX2ly5IfA}r@R{jZp=bxvBzHv1*)Zf@+d#vZ_+`57k1|qpDWb^Qu==+f=Wq-cY@zdRz65YPafu z>X7P~>bUBp+Ne%dJJem(x$1m%iF%6Kr}nEG)IoJvJzL$RzE8bKy;%LAda3$h^)u>L z^&0hB^*Z%>^-=XH_1Eg3)xWBLS6@}=)iY7(l)b!94X(nsDni-mUO+Yh8GgmWD)2vybS)^H_c}TNV^RVVo&EuLU zHP31`YPM^3Xm)CLY2MYG(tM@4pe2ycCu%daqPC+}(stGsX(wx^YlGTm?S0yZwXNFc zwd=JTwVSltw6AI3(C*eA&>qsB*Iw3XbY`7R*HxFL%hC1H_0i?(`s)VhM(Rp+6Lr-( zkIt`a(1mnV_b=T--BMkvZoO`cZmaHX-Fv!&y5qX&*tppA z*o;_vY@gWN*#5CM#oiJ-I(A}gb*wLTX6*dfm9eX0SI4f2eIfS6*v+vAV?T^N5_>H6 zcWBe{oALoec8rLf>FK$ZQ)VQj+nz(6kwQ=6Kmbliq)p2X$ z*2b-iTOYSEZgbqrLZfhxaG$UcLUf7nwD6p;NqAk@BODY?2xo<#g{vmD$!h9my4f_) zlxG@Z8fvOC)tIK4>P*v3GfefS+e~+u7MSif-D6r|+F{yh+GW~f+G~2x^nvM=={K{^ z9BVe31+&@Q!<=pIY3^$>xOs#5b@P7n=jQX~AI(3Re=+}NzG%K| zz8a6?xp+;yDc%v^A-;QjkNBMU0rC0qqvI#Wd*c1^&G8H4ABbNP|4{t0_$~4OjekA< z&G@(C-;UoIzbk%E{O1Yj2|W^W6Z$9IlyFPJpoGB*`3VIHBNECIrYHCk<|f>pa8E*O z!ls0m61F64O?Wk7U&6-;pC+72IGu1NkxNu1#w2PJ^@*{G#zZNxB5_LM)WoX9n#5^| zb&1mxXC=-_{8!?=iT5WiN?e?{J@Ng-6N%>%FC>wqxTKV%u1N!v3X{erjZ2!4G%0Cv zQdLq-(zK+yr0Ge%B!AM~NsE&nPkJ)x*`)PJo0E1W?N9nB=|s|JN#~NjPP&+MIq9ke zTR4l#5@XR?T$WCjE|zYVOiQ*U$I{DEWC>ViT0)ja%WO-NWuE0J%X-U3%O=Za%O1-Y zma~?xEZ+eDcKPisUKDQsSl(+lKM>Q+SCoHFQmShx;6Ec z)OS*Mr@ouIFZHw3bE)T3FQ=)~Olj7%PHEX`xoM-*D$<(LZcDo>?Xk4wY1`BGrX5cE zH0^3SPUq59=`rcrbbY!Zy?c7E^pWWk(%tFR>C@8d(r2XCr!Po+cz=40DDpqf17QjO>j583QwhXN=D9WX#RDD`Q>8ri=p_KW6-t@k_>U z85c7yXI!;mTe>aFcC&4uE#FpP8*UqAE4Gccd2Ii*&9^;ad&>5Vt<|>D_N?tW+l#iX zw!^lgwvTKlY#-Y`wVkqE7ENNNm?h?jy~I9ZuGn9!6C1^);%4z>@fC5K_?q~JxLw>K z?iUY=hsC4faq*=1yLd^wVn=pj=k02H7yB@KiG7T{**@RiV!z#fr~NMbzwGzg@3$|q zFSb8uUuu8Y{;2(N`;+#k?aS>e?5ph0+c(%>u)ks7XFqK}WB<~A*8YS2g8eu9Mf+uk z;OOA!?&#$xc8qsSc1&~BIUaN@bv*2N)bY6ENypQU<&G7ORgTq;HIB88b&mCpjgC!@ zmmFIhTOF@Db~tuAesuig_{H&?!Ip6P*@kiZjh= zb-J9LoMWBioD-aroRgiEPPen#>2cONz0MiVdS}2n(;0F$I%hkZob#N`&iT#_&TY=! z&T}199XfXy)?r46IUOGASlMxY$9p<1?bzCJeaF{2?(cZM;}utTS07idtDkGIE8jKL zRpcshJ??ta^|WibYlUl-Yqe{QYprXYYrSiuYm@6G*A~}S*Q>7ox?Xp^>3Ylcw(A|& zZr8i6eXjRi`&|cJAG(gXj=7GzPP#sEedhYy^|k9;sh>1Jx>*`1*sa|T3Leea$N&1I0U%E}YQ@UHaS6V17mL8Oz zke-sBky@pd(zDWY(!ZtuNY6_fq!*+YrOndI(ks$7={4yMX}h#T+9~al_DFlB_oNS` z1JWVsuyj=VNcvp*pL9n0PC76BAYGI$OIJF@bc$7WbqQNehChC$@*Dl`l=lAsX;-&A literal 0 HcmV?d00001 diff --git a/pt.lproj/SUUpdatePermissionPrompt.strings b/pt.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..82477d56cf5e615810dc4fb8a89e89047643b324 GIT binary patch literal 3036 zcmeHIO;5r=6r3}^qVZzF0X2Sr5@QfFAt7KQC*!45h{#6~T9rSq&MYjNN^J=-93-S{ zyF2ggzM1#le!OR}hb$bF(T9r(e-6(1?4XWYG||Qz0$iiZw>}<-)7a;Fk*I+40l(S{ zD{CQ--;oWp@WdI9_$n$3{1J}lzOpXjCstl=urR}4qHj8<7_nV4nlZ_(x> z(Z>0)(Z#JNM`QCieO-OU;h@9##*8h*h!rVS+oo>aSfwvgXkCazzy6Mvr}aJk1)~)- z$YGuTO^!8=W!6fK-RTZjDfl{#@Rj7xvXO*Kj&+qOvgsr5Z2bGdpYmR4lYA(O*-&DtI;1vD@A zr9qqO=r;BFuYT313bPgIp_^Iwj;ZgE`bbIX`C_m>F$2C0O~-qsmoa+C5p!cS)V(eX zk1N8s<`%a8yY%ZY1C$Ng-8EiknKI?fGw0P`ba%*JXLfm;3$G3v&_ + + + + IBClasses + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib b/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..5132e29f2 --- /dev/null +++ b/ru.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,18 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + IBSystem Version + 9E17 + targetFramework + IBCocoaFramework + + diff --git a/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib b/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib new file mode 100644 index 000000000..5220a221f --- /dev/null +++ b/sv.lproj/SUUpdatePermissionPrompt.nib/classes.nib @@ -0,0 +1,59 @@ + + + + + IBClasses + + + CLASS + SUWindowController + LANGUAGE + ObjC + SUPERCLASS + NSWindowController + + + ACTIONS + + finishPrompt + id + toggleMoreInfo + id + + CLASS + SUUpdatePermissionPrompt + LANGUAGE + ObjC + OUTLETS + + delegate + id + descriptionTextField + NSTextField + moreInfoButton + NSButton + moreInfoView + NSView + + SUPERCLASS + SUWindowController + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib b/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib new file mode 100644 index 000000000..c5a067e89 --- /dev/null +++ b/sv.lproj/SUUpdatePermissionPrompt.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../Sparkle.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 6 + + IBSystem Version + 10A96 + targetFramework + IBCocoaFramework + + diff --git a/zh_CN.lproj/SUUpdatePermissionPrompt.strings b/zh_CN.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000000000000000000000000000000000..88b715f9678eecfc3e933a805b9c21ae5900c1dc GIT binary patch literal 2628 zcmeHIOKTHR6h0R0qR@p~Hx6zrU8E5ojTVubC=^PfO~6OuV=|qjF^{RuOf*o$mM&Tf zzQCP7K-XK{RvjB>LX|Em!_1v~?>Xn5^L^)>yY;P4hGb9- ziAzOHspB!^9Nwdnl(b~TmIva>B`M*rB9oBgSi$}Xq+G1KnAzJ}T7wpUmP|=r?qbD) z{Fo%ROaiL~Lrk$ZQE>shju`3WZ~chk2%BZRO;j;u-eE z)5(tZzdsq)d!3o#E9HmZh7=Ib3}W$Q8Z}0qY*mS7NAAvtU@zv2cK0W0Tr{tr3#;~# ztuX9%k558UKqQH82IDx!IC6O$-Q$K_3=#bqU-UutbZkL#e>aoIP|X&q(F5=7q9*37 z@Fwcj!7BT49kaU1i7IB($P@<|Q&N35DCc7m_2IDl6E#u?(;NBs_UdwfW^?x4!}-#i zEB_`FM!Uqs05-LDP3f*jOu0@xiK-2}d4DG7-2xKlRR(O+A!@>?_Om**R`z6>lz$y9V>87&U;=YNMdst`vvs+W2 zKAX5dDbJ4QLWhw=B!^wTnf8i~kP$L9w#HWUQZm#ZzPwp&#m5^U0i6$?X~p!&0MM{I)D9T@oRRn ud@Vb0^_lnS#h2yGqs>`L_e6Duy?ruN=*`V7tt_sk4?JCH(DGr``M(20n9&;m literal 0 HcmV?d00001