Removed Sparkle from main repository

CQTexperiment
Chris Moeller 2015-07-31 23:32:56 -07:00
parent c9bee1e7ae
commit da9723fbf1
291 changed files with 0 additions and 33776 deletions

View File

@ -1,45 +0,0 @@
AccessModifierOffset: -4
AlignEscapedNewlinesLeft: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackParameters: true
BreakBeforeBinaryOperators: true
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
#DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: foreach,Q_FOREACH
IndentCaseLabels: true
IndentFunctionDeclarationAfterType: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
#PointerAlignment: Right
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
Standard: Cpp03
TabWidth: 4
UseTab: Never

View File

@ -1,36 +0,0 @@
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.xcuserstate
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

View File

@ -1,2 +0,0 @@
language: objective-c
script: make travis

View File

@ -1,333 +0,0 @@
# 1.10.0
* Massive improvements to the BinaryDelta tool (Zorg)
- Ability to track file permissions (Zorg)
- Nicely formatted log output (Zorg)
- Numerous bug fixes in handling of symlinks, empty directories, case-insensitive names, etc. (Zorg)
- Refactored and modernized code (Zorg)
- libxar is no longer weak-linked (C.W. Betts)
* Double-check the code signature of the the app after installation (Isaac Wankerl)
* Added headless guided package installation (Graham Miln)
* Added ability to inject custom HTTP headers in appcast request (Mattias Gunneras)
* Changes to make unarching more reliable (Zorg, Kornel Lesiński)
* Have Sparkle build a framework module (C.W. Betts)
* Stdout used for non error outputs (JDuquennoy)
* French locale update (Kent Sutherland)
# 1.9.0
* Added SUUpdater delegate method for failures. (Benjamin Gordon)
* Make the error definitions public (C.W. Betts)
* Add support for lzma compressed tarballs (Kyle Fuller)
* Back to SKIP_INSTALL=YES by default (Tony Arnold)
* Properly set install names and rpaths for targets (Jake Petroules)
* Use Library/Caches rather than app support directory (Kornel Lesiński)
* Check for a modal window being onscreen before trying to put up the Sparkle prompt (Alf Watt)
* Fixed crashes on 10.7 (Chris Campbell, Ger Teunis)
* Fixed Sparkle tags parsing (Tamás Lustyik)
* SULog code cleanups (Kevin Wojniak)
* Make sure CFBundleVersion is a semantic version number. (Jake Petroules)
* Replace typedef enums with typedef NS_ENUM to make Swift happier (C.W. Betts)
* Fix warnings under Xcode 6.1 relating the SUUpdateAlert XIB (Tony Arnold)
* Prefer string constants to strings (Jake Petroules)
* Use Info.plist keys instead of macros (Jake Petroules)
* Only export public symbols. (Jake Petroules)
* BinaryDelta: avoid crash with bad paths (Jake Petroules)
* Fixing Swedish translations (Erik Vikström)
* Turkish localization fixes (Emir)
* Proofing of Ukrainian localization (Vera Tkachenko)
# 1.8.0
* New SUDSAVerifier based on up-to-date OS X APIs (Zachary Waldowski)
* Detailed error log for failed signature checks (Kornel Lesiński)
* Converted Sparkle to ARC (C.W. Betts)
* Converted ivars to properties. (Jake Petroules)
* Cocoapod support (Xhacker Liu)
* Quarantine removal on OS X 10.10 (C.W. Betts)
* Updated Japanese localization (1024jp)
* Added Greek localization
# 1.7.1
* Removed option to install unverified updates (Kornel Lesiński)
* Added detailed log when code signing verification fails (Sam Deane)
* Restored original Sparkle icon. (Jake Petroules)
* Switched SUUpdateAlert.xib to AutoLayout (Kornel Lesiński)
* Replace references to andymatuschak.org with sparkle-project.org. (Jake Petroules)
* Several code cleanups, modernizations, fixed warnings and improved code formatting (Jake Petroules)
* Make the repository significantly more organized. (Jake Petroules)
* Xcode project: set organization name and class prefix. (Jake Petroules)
* Link to Foundation and AppKit instead of Cocoa. (Jake Petroules)
* Use new operatingSystemVersion API when available. (Jake Petroules)
* Add .clang-format configuration file for source code formatting. (Jake Petroules)
* Add a target to build Sparkle API documentation using Doxygen. (Jake Petroules)
# 1.7.0
* Dropped support for OS X 10.6. Sparkle now supports 10.7 and newer (including 10.10 Yosemite) on 64-bit Intel Macs (the last 32-bit Mac was released in 2006).
* Removed use of deprecated functions (Zachary Waldowski)
* Switched to modern Obj-C runtime and new literals syntax
* Removed pre-10.7 code. (C.W. Betts)
* Use more Blocks/libdispatch code. (C.W. Betts)
* Cleaned up and improved security of `generate_keys`/`sign_update` scripts
# 1.6.1
* Removed archive password prompt (Kornel Lesiński)
* (Re)fixes bug where URLs are naively double escaped (Andrew Madsen)
* Fixed typo that caused crashes in BinaryDelta (Tamas Lustyik)
* SUStandardVersionComparator.h is public (Vincent CARLIER)
* Remove pre-10.6-specific code. (C.W. Betts)
* Objective C 2 getters and setters. (C.W. Betts)
* Define correct dependencies on locale scripts (Antonin Hildebrand)
# 1.6.0
* Cleaned up and deleted redundant strings files (Kornel Lesiński)
* Modern Objective C syntax, properties where possible. (C.W. Betts)
* Make SUAppcastDelegate a formal protocol. (C.W. Betts)
* Fixed default font in release notes WebView (Kornel Lesiński)
* Configurable name for finish_installation.app (Kornel Lesiński)
* Removed code for 10.4 (Kornel Lesiński)
* Convert all strings files to UTF-8 (UTF-16 must die) (Kornel Lesiński)
* Removing GC target (Matt Thomas)
* finish_installation.app and pkg files will not removed when we use *.pkg installer and restart system in the installer (Takayama Fumihiko)
* Select Korean and Slovak for Sparkle.strings localization (Shon Frazier)
* Updated the Romanian translation (Gabe)
* pt-BR localization polishing (BR Lingo)
* update zh_CN (61)
* Shut up some warnings & make build with newer Xcode (Uli Kusterer)
* Less unsafety with format strings (Uli Kusterer)
* New icon (Rick Fillion)
* fixed a 'content rectangle not entirely onscreen' warning (Simone Manganelli)
* updated sends system profile to use info.plist if user defaults key isn't present (Jamie Pinkham)
* Support for notifications on some updater events (Doug Russell)
* Allow the delegate to trigger a silent install and relaunch (Matt Stevens)
* Support silent relaunches (Matt Stevens)
* Increment the sudden termination counter if installing on quit (Matt Stevens)
* Prompts the user to update after a week (rather than a day) if he doesn't quit the app (Andy Matuschak)
* Adding <sparkle:tags> appcast item element, <sparkle:criticalUpdate /> tag (Andy Matuschak)
* We have this check box that says "Automatically download and install updates in the future." But we only download them automatically. We still ask permission again before installing them. (Andy Matuschak)
# 1.5.0-beta6
* Important Changes
* Sparkle now requires DSA signatures on your updates. Check the documentation for more information on how to set that up if you don't already sign your updates. You can bypass this requirement if you deliver both your appcast and your updates over SSL.
* Sparkle will no longer display release notes located at file:// URLs, since Javascript on such a page would be able to read files on your file system.
* For security reasons, Sparkle will refuse to install updates which appear to "downgrade" the app.
* SUUpdater now implements new keys: "automaticallyDownloadsUpdates", "lastUpdateCheckDate", and "sendsSystemProfile."
* Fixed a bug that could prevent SUProbingUpdateDriver from working.
* Fixed a bug that prevented the updaterWillRelaunchApplication: delegate method from getting called.
* Fixed displaying release notes transmitted "loose" in the <description> key.
* Fixed Sparkle compilation on 10.4 systems.
* Fixed a bug that could cause window confusion if an app changed its LSUIElement at runtime.
* Added support for Sparkle 1.1's behavior of disabling updates when the check interval is 0.
* Sparkle can now handle appending parameters to URLs which already have parameters.
* If an update's sparkle:shortVersionString is the same as the host's CFBundleShortVersionString, the sparkle:version and CFBundleVersion will be presented in parentheticals.
# 1.5.0-beta5
* Important Changes!
* Made every Sparkle class private except for SUUpdater, SUAppcast, SUAppcastItem, and the SUVersionComparisonProtocol.
* There is now a single SUUpdater singleton for every host bundle; instead of -[SUUpdater setHostBundle], you can use +[SUUpdater updaterForBundle].
* Redefined the (entire) delegate protocol accordingly.
* Renamed -[SUUpdater updatePreferencesChanged] to -[SUUpdater resetUpdateCycle]. This provides better semantics for non-apps, which need to start the update cycle manually.
* -[SUUpdater checkForUpdatesWithDriver] is private. If you were using SUProbingUpdateDriver, you can now use -[SUUpdater checkForUpdateInformation] for a similar effect.
* All the user defaults keys are now private; instead, SUUpdater is KVC-compliant for automaticallyChecksForUpdates, updateCheckInterval, and feedURL.
* Reduced the size of the English-only framework by 25%.
* System profiling information is now only submitted to the server once per week; this will help normalize your statistics across users with different interval preferences.
* The feedParamatersForUpdater: delegate method now requires "displayKey" and "displayVersion" keys so that it can inform the user what's being sent.
* Added a delegate method called pathToRelaunchForUpdater: which can be used for plugins to provide the path which should be used when relaunching the client after installing an update.
* Added support for xml:lang to pick localized nodes in appcasts (for release notes, etc).
* Fixed a bug which would cause the "checking for updates" window to not disappear in certain extraordinary error conditions.
* Fixed a DSA signature checking bug for .tar.gz archives.
* Sparkle now refuses to update on any read-only volume, not just dmgs.
* Sparkle will clean up the host app's name and version before sending it as the user agent string; some non-ASCII characters were causing problems.
* Added an Italian localization courtesy Michele Longhi.
* Added a Swedish localization courtesy Daniel Bergman.
* Fixes to the French localization courtesy Ronald Leroux and Yann Ricqueberg.
* Fixes to the German localization courtesy Sven-S. Porst.
* Fixes to the Russian localization courtesy Alexander Bykov and Anton Sotkov.
* Fixed a number of issues related to archive format detection: I reverted back to extensions from UTIs.
* Focus behavior fixes for LSUIElement apps.
* The status window progress bar now animates even when indeterminate.
* Major refactorings to improve functionality for non-app bundles.
# 1.5.0-beta4
* Fixed a critical bug which prevented non-.dmgs from unarchiving properly.
* Added reporting of 64-bit capability to the profiling system.
# 1.5.0-beta3
* Added a new delegate method to SUUpdater.h to allow delegates to specify custom version comparators.
* Added a German localization, courtesy the Camino localizer team: Dominik Tobschall, Tobias Stohr, and Friedemann Bochow.
* Bug fixes:
* Fixed a serious bug which could cause a server to be DDoS'd (or the host app to crash!) if an appcast fails to be parsed.
* Fixed .tbz extraction if the archive was made with Stuffit.
* Fixed support for .tar.bz2 and .tar.gz; Sparkle has to assume the archive is a tar when it sees "bz2" and "gz"; don't use those without tarring.
* Fixed a typo which caused the shouldPromptForPermissionToCheckForUpdatesToHostBundle: method to not work in 1.5b2.
* Fixed .zip extraction on Tiger (Apple changed the UTI between releases)
* Fixed a crasher on Tiger.
* Fixed display of the default app icon when the host app doesn't have an icon.
* Sparkle now displays a sensible progress string and uses an indeterminate progress bar when the server doesn't report a file size.
* Fixed some memory leaks.
# 1.5.0-beta2
* Compatibility Issues:
* Most of the delegate method selectors have changed to scale better. See SUUpdater.h for changes; you'll likely have to make changes if you implement any delegate methods.
* If you're using .tar.gz or .tar.bz2 archives, name them ".tbz" or ".tgz" instead; Sparkle now uses UTIs for archive detection, and it's not smart about double extensions.
* I'm no longer supporting 10.3. This may or may not work on Panther—probably not.
* Sparkle's no longer built for ppc64 by default. If you want to ship that, feel free to build your own, but this saves a few hundred k.
* Enhancements:
* Sparkle now detects if the preferences for automatic update checks or the time interval change mid-cycle.
* If your product is a non-.app, you need to clue Sparkle in on the change by calling [[SUUpdater sharedUpdater] updatePreferencesChanged].
* Added a cancel to the "checking for updates..." dialog.
* Sparkle now cleans up all its litter in /tmp.
* Made SUUpdater's delegate an IBOutlet so you can hook it up in IB.
* Bug fixes:
* Sparkle no longer crashes on non-GC hosts when the user cancels an update's downloads.
* Sparkle no longer gets stuck in an inconsistent state or crashes when it can't parse the appcast on scheduled updates.
* Added the sharedUpdater method to SUUpdater, as it should have been.
* Fixed a bug where the "checking for updates..." window wouldn't go away if an error occurs while checking for updates.
* Made the dual-mode build configuration actually use the .xcconfig which builds it with GC support. (oops!)
* Fixed relaunching for prefpanes.
* Sparkle no longer fails to install updates on Snow Leopard (though there's still an issue with trashing the old version of the app, but it seems to be a 10.6 bug)
* Sparkle now handles redirects correctly under Tiger.
* Fixed the installation path for non-.app bundles.
* Fixed a bug which could crash Sparkle under non-English locales.
* Fixed a weird race condition which could cause the relaunch tool to never notice that its target relaunched.
* Fixed a bug where if the host app is inactive when an update occurs, the update alert sometimes doesn't become key.
* Minor textual fixes.
* Localizations:
* Dutch: Maarten Van Coile
* French: Yann Ricquebourg
* Spanish: Ernesto Gomez Cereijo
# 1.5.0-beta1
* The most important things to know:
* The 10.3 support is untested at best; sketchy at worst. Test with it thoroughly before you use it.
* Sparkle now asks for permission to update on second launch; don't be surprised at that. You can change that behavior with a delegate method; read SUUpdater.h for more info.
* We no longer distinguish between "check on startup" and "scheduled updates"; everything is scheduled, with the default being every day.
* The test application is using the new profiling features, but that's only for demonstration: these are off by default. More on this later.
* There are no localizations yet.
* New features:
* Sparkle now supports .pkgs. Just name the .pkg the name of the app and put in the update archive.
* Sparkle now sends optional demographic profiling information; set SUEnableSystemProfiling to YES in your Info.plist and check out the GET data sent to your webserver when fetching the appcast. More on this in the documentation. The test application has this on so you can see the behavior.
* Sparkle now supports updating non-.apps. Just call -setHostBundle: on the global SUUpdater to let it know what you're trying to update.
* Sparkle now supports garbage collection in the host app. Use "Sparkle-with-GC.framework" for that, but be aware it's 10.5-only.
* Sparkle is now 64-bit compatible, compiling both ppc64 and x86_64.
* Sparkle now supports a sparkle:minimumSystemVersion key you can set on appcast items. It does what you think it does.
* Sparkle now checks to see if the host app is running from a disk image and refuses to update if it is. (10.4+ only)
* Added support for entities in enclosure paths.
* The file size output is now formatted prettily.
* Sparkle now gives visual indication that it's checking for updates when the update's user initiated. ie: it pops up a status controller saying "checking for updates..."
* Added support for an SUPublicDSAKeyFile, so people don't have to copy/paste their entire key into their Info.plist. Set this key in your Info.plist to the filename of the key in your Resources directory.
* Added an actually maintainable codebase.
* Changes:
* Sparkle version comparison is now dramatically less stupid and verified by a bunch of unit tests. If something doesn't work the way you think it should, add a test to SUVersionComparisonTest.m
* Added a minimum to the check interval so that developers don't accidentally release their apps into the wild with 60-second test check intervals and have DOS-attack-like results. It's an hour now for release mode; feel free to change it.
* The relaunching process now uses a separate helper app, which is a much more robust method.
* Changed CFBundleShortVersionString behavior: Sparkle no longer uses Apple's about box style of displaying ShortVersionString (CFBundleVersion) when the latter is available.
* No more MD5 checking. Use DSA: it's actually secure.
* The abomination that was SUStatusChecker is dead. Use SUProbingUpdateDriver instead.
* Bugfixes:
* Fixed a huge bug with fully-automatic updating: before, if the user chose to relaunch later, the app would be running from the trash for a while. Now the buttons are "install and relaunch" or "install later."
* Sparkle forces Spotlight to reindex the updated app so that it won't keep pointing to the one in the trash.
* Sparkle trims whitespace from around DSA signatures; this could cause crashes before.
* Fixed a bug where the user choosing to skip a version would inhibit future automatic updates until the next launch.
* Fixed a bug that could occur when the app has a localized CFBundleName.
* .dmgs now work on Leopard.
* The status controller's button now sizes appropriately to the localization.
* Sparkle now works correctly with LSUIElement apps: it focuses them before displaying the update alert.
* Sparkle now deletes failed partial downloads.
* The update alert no longer floats above everything in the app.
* Fixed varied and sundry memory leaks.
* A ton of other things that I've forgotten or were too small to mention!
# 1.1
* Optimized framework size: now only 1.4mb with all localizations and 384kb with only English (an English-only version is in the Extras folder).
* Added a new SUStatusChecker class for programmatically determining if a new version is available (see the docs); thanks, Evan Schoenberg!
* Added support for apps using SIGCHLD; thanks, Augie Fackler!
* Added a zh_CN update from JT Lee
* Added a Polish update from Piotr Chylinski
* Fixed DMG support for images with /Applications symlinks.
* Fixed a really stupid interval-checking bug that could cause repeated hits to the appcast.
* Fixed a bug where the check interval would be inconsistent if a value of 0 was stored in the user defaults.
# 1.0
* Additions:
* Added real version comparison courtesy Kevin Ballard: Sparkle now knows that 0.89 < 1.0a3 < 1.0.
* Added many localizations courtesy David Kocher's localization team.
* Added a much better installation mechanism courtesy Allan Odgaard.
* Added a user agent string to the RSS fetch request.
* Added support for CFBundleShortVersionString in addition to CFBundleVersion, and support for a sparkle:shortVersionString attribute on the enclosure.
* Added support for CFBundleDisplayName if available.
* Changes:
* Automatic updating is now allowed by default, but only if DSA signing is on.
* Pressing Escape or closing the update alert now reminds the user later.
* Now when there's a stored check interval, Sparkle doesn't check immediately on startup the first time the app is launched because the user hasn't consented to it yet.
* The update alert now remembers its size and floats.
* Bug Fixes:
* Fixed installation of DMGs with multiple files enclosed.
* Fixed a nasty memory leak.
* Fixed a bug wherein having no value for allowing automatic updates would display a checkbox for the updates but would not honor it.
* Fixed a bug in zip extraction that occurred in Panther.
* Fixed release notes caching.
* Fixed a bug wherein Sparkle refused to authenticate the installation if the user had cancelled authentication previously in that session.
* Fixed a weird bug that would cause a second help menu to appear on first launch.
* Fixed a bug that could occur when changing the scheduled check interval.
* Fixed a bug wherein the host app could crash if the user clicked Remind Me Later before the release notes finished loading.
* Fixed a bug wherein the behavior was undefined if the user manually initiated a check when an automatic one was already taking place.
* Fixed wrapping on the description field in the update alert.
# 1.0-beta3
* Fixed a nasty crasher that occurred often when the user was not connected to the internet.
# 1.0-beta2
* Major Improvements:
* Fully automatic updating! (see the Documentation: this is beta and off by default)
* Added support for DSA signatures (see the Documentation).
* Added support for MD5 sum verification.
* Added Security.framework-based authentication for installing to privileged directories.
* Huge refactoring of the codebase: there's now a Sparkle Xcode project, Sparkle is now a framework, and everything is modular / abstracted. And no more code-generated interface.
* Minor Improvements:
* A SUUpdaterWillRestartNotification is sent out before restarting now.
* Added key equivalents to alert panel buttons.
* Error handling is much prettier now: technical messages are not presented to the user anymore.
* There's now a test app for developers to see what Sparkle's like before using it.
* Wrote new, pretty, extremely thorough documentation.
* Bug Fixes:
* Relaunch behavior is much improved and shouldn't fail in huge apps anymore.
* Fixed a bug wherein a failing tar command could crash the host app.
* Sparkle now looks at InfoPlist.strings in addition to Info.plist.
* Fixed some stupid typos.
# 1.0-beta1
* Major New Features:
* Sparkle now supports scheduled periodic updates—read the Readme for information on how to use it.
* Sparkle now supports WebKit-based release notes (for CSS and full HTML), which it displays in the main update alert, not a separate panel. The Readme has much more information. Sparkle will, of course, fall back on NSTextView if the host app does not include WebKit.
* Minor New Features:
* Added support for .zip update archives.
* Added support for .dmg update archives.
* Implemented Remind Me Later to replace simple update cancellation.
* Implemented Skip This Version functionality.
* Added support for multiple feeds via the user defaults SUFeedURL key taking precedent over the one in Info.plist.
* Added support for Sparkle's custom XML namespace, which is optional but may prove useful. See the Readme for more information.
* Bug Fixes:
* Sparkle will no longer enter an inconsistent state if the user tries to update again while one is already in progress.
* Sparkle now uses CFBundleName to determine the application's name instead of the app's filename.
* Sparkle no longer crashes if the user cancels during extraction.
* Lots of code refactoring.
# 0.1
* Initial Release

View File

@ -1,3 +0,0 @@
// BinaryDelta tool only
PRODUCT_NAME = BinaryDelta

View File

@ -1,2 +0,0 @@
#include "ConfigBinaryDelta.xcconfig"

View File

@ -1 +0,0 @@
#include "ConfigBinaryDelta.xcconfig"

View File

@ -1,77 +0,0 @@
// Common
SPARKLE_BUNDLE_IDENTIFIER = org.andymatuschak.Sparkle
SPARKLE_RELAUNCH_TOOL_NAME = Autoupdate
SPARKLE_APPEND_VERSION_NUMBER = 1
// Sparkle usually doesn't allow downgrades as they're usually accidental, but
// if your app has a downgrade function or URL handler, turn this on
SPARKLE_AUTOMATED_DOWNGRADES = 0
// If your app file on disk is named "MyApp 1.1b4", Sparkle usually updates it
// in place, giving you an app named 1.1b4 that is actually 1.2. Turn the
// following on to always reset the name back to "MyApp":
SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME = 0
SPARKLE_VERSION_MAJOR = 1
SPARKLE_VERSION_MINOR = 10
SPARKLE_VERSION_PATCH = 0
SPARKLE_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH)
CURRENT_PROJECT_VERSION = $(SPARKLE_VERSION)
ALWAYS_SEARCH_USER_PATHS = NO
GCC_PRECOMPILE_PREFIX_HEADER = YES
GCC_PREFIX_HEADER = Sparkle/Sparkle.pch
GCC_SYMBOLS_PRIVATE_EXTERN = YES
GCC_INLINES_ARE_PRIVATE_EXTERN = YES
ARCHS = $(ARCHS_STANDARD)
MACOSX_DEPLOYMENT_TARGET = 10.7
GCC_PREPROCESSOR_DEFINITIONS = SPARKLE_BUNDLE_IDENTIFIER=\"$(SPARKLE_BUNDLE_IDENTIFIER)\" SPARKLE_RELAUNCH_TOOL_NAME=\"$(SPARKLE_RELAUNCH_TOOL_NAME)\"
CLANG_ENABLE_OBJC_ARC = YES
// Enable warnings
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES
CLANG_ENABLE_MODULES = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES
CLANG_WARN_OBJC_RECEIVER_WEAK = YES
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
GCC_WARN_ABOUT_MISSING_NEWLINE = YES
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES
GCC_WARN_PEDANTIC = YES
GCC_WARN_SHADOW = YES
GCC_WARN_SIGN_COMPARE = YES
GCC_WARN_STRICT_SELECTOR_MATCH = YES
GCC_WARN_UNDECLARED_SELECTOR = YES
GCC_WARN_UNKNOWN_PRAGMAS = YES
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_LABEL = YES
GCC_WARN_UNUSED_PARAMETER = YES
GCC_WARN_UNUSED_VARIABLE = YES
// These should be removed once the conversion to ARC is complete
WARNING_CFLAGS_EXTRA = -Wno-custom-atomic-properties -Wno-implicit-atomic-properties
// Turn on all warnings, then disable a few which are almost impossible to avoid
WARNING_CFLAGS = -Wall -Weverything -Wno-empty-translation-unit -Wno-unused-macros -Wno-gnu-statement-expression -Wno-receiver-is-weak -Wno-arc-repeated-use-of-weak -Wno-auto-import -Wno-cstring-format-directive $(WARNING_CFLAGS_EXTRA)

View File

@ -1,11 +0,0 @@
#include "ConfigCommon.xcconfig"
// Debug only
GCC_OPTIMIZATION_LEVEL = 0
DEBUG_INFORMATION_FORMAT = dwarf
GCC_GENERATE_DEBUGGING_SYMBOLS = YES
SPARKLE_EXTRA_DEBUG = -DDEBUG
OTHER_CFLAGS = $(SPARKLE_EXTRA_DEBUG)
ONLY_ACTIVE_ARCH = YES
COPY_PHASE_STRIP = NO

View File

@ -1,10 +0,0 @@
#include "ConfigCommon.xcconfig"
// Release only
GCC_OPTIMIZATION_LEVEL = s
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
GCC_GENERATE_DEBUGGING_SYMBOLS = YES
DEAD_CODE_STRIPPING = YES
GCC_TREAT_WARNINGS_AS_ERRORS = NO
GCC_WARN_UNINITIALIZED_AUTOS = YES

View File

@ -1,13 +0,0 @@
// Framework only
DYLIB_INSTALL_NAME_BASE = @loader_path/../Frameworks
DYLIB_COMPATIBILITY_VERSION = 1.6
DYLIB_CURRENT_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH)
PRODUCT_NAME = Sparkle
WRAPPER_EXTENSION = framework
FRAMEWORK_VERSION = A
INFOPLIST_FILE = Sparkle/Sparkle-Info.plist
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) BUILDING_SPARKLE=1
OTHER_LDFLAGS = -Wl,-U,_NSURLQuarantinePropertiesKey
SKIP_INSTALL = YES
DEFINES_MODULE = YES

View File

@ -1,4 +0,0 @@
#include "ConfigFramework.xcconfig"
// Unit tests need access to non-public classes
GCC_SYMBOLS_PRIVATE_EXTERN = NO

View File

@ -1,2 +0,0 @@
#include "ConfigFramework.xcconfig"

View File

@ -1,8 +0,0 @@
// Relaunch Tool only
INFOPLIST_FILE = Sparkle/Autoupdate/Autoupdate-Info.plist
PRODUCT_NAME = $(SPARKLE_RELAUNCH_TOOL_NAME)
SKIP_INSTALL = YES
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
CLANG_ENABLE_MODULES = NO
OTHER_LDFLAGS = -Wl,-U,_NSURLQuarantinePropertiesKey

View File

@ -1 +0,0 @@
#include "ConfigRelaunch.xcconfig"

View File

@ -1 +0,0 @@
#include "ConfigRelaunch.xcconfig"

View File

@ -1,8 +0,0 @@
// Test Application only
INFOPLIST_FILE = TestApplication/TestApplication-Info.plist
PRODUCT_NAME = Sparkle Test App
WRAPPER_EXTENSION = app
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
CLANG_ENABLE_OBJC_ARC = NO
LD_RUNPATH_SEARCH_PATHS = @executable_path/../Frameworks

View File

@ -1,2 +0,0 @@
#include "ConfigTestApp.xcconfig"

View File

@ -1 +0,0 @@
#include "ConfigTestApp.xcconfig"

View File

@ -1,10 +0,0 @@
// Unit Test only
INFOPLIST_FILE = Tests/SparkleTests-Info.plist
PRODUCT_NAME = Sparkle Unit Tests
WRAPPER_EXTENSION = xctest
OTHER_CFLAGS = $(inherited) -iframework"$(DEVELOPER_FRAMEWORKS_DIR)" -iframework"$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_SYMBOLS_PRIVATE_EXTERN = NO
WARNING_CFLAGS = $(inherited) -Wno-variadic-macros -Wno-gnu-zero-variadic-macro-arguments
FRAMEWORK_SEARCH_PATHS = $(inherited) $(DEVELOPER_FRAMEWORKS_DIR)
LD_RUNPATH_SEARCH_PATHS = @loader_path/../Frameworks

View File

@ -1 +0,0 @@
#include "ConfigUnitTest.xcconfig"

View File

@ -1 +0,0 @@
#include "ConfigUnitTest.xcconfig"

View File

@ -1,27 +0,0 @@
#!/bin/bash
set -e
if [ "$ACTION" = "" ] ; then
rm -rf "$CONFIGURATION_BUILD_DIR/staging"
rm -f "Sparkle-$CURRENT_PROJECT_VERSION.tar.bz2"
mkdir -p "$CONFIGURATION_BUILD_DIR/staging"
cp "$SRCROOT/CHANGELOG" "$SRCROOT/LICENSE" "$SRCROOT/Resources/SampleAppcast.xml" "$CONFIGURATION_BUILD_DIR/staging"
cp -R "$SRCROOT/bin" "$CONFIGURATION_BUILD_DIR/staging"
cp "$CONFIGURATION_BUILD_DIR/BinaryDelta" "$CONFIGURATION_BUILD_DIR/staging/bin"
cp -R "$CONFIGURATION_BUILD_DIR/Sparkle Test App.app" "$CONFIGURATION_BUILD_DIR/staging"
cp -R "$CONFIGURATION_BUILD_DIR/Sparkle.framework" "$CONFIGURATION_BUILD_DIR/staging"
# Only copy dSYMs for Release builds, but don't check for the presence of the actual files
# because missing dSYMs in a release build SHOULD trigger a build failure
if [ "$CONFIGURATION" = "Release" ] ; then
cp -R "$CONFIGURATION_BUILD_DIR/BinaryDelta.dSYM" "$CONFIGURATION_BUILD_DIR/staging/bin"
cp -R "$CONFIGURATION_BUILD_DIR/Sparkle Test App.app.dSYM" "$CONFIGURATION_BUILD_DIR/staging"
cp -R "$CONFIGURATION_BUILD_DIR/Sparkle.framework.dSYM" "$CONFIGURATION_BUILD_DIR/staging"
fi
cd "$CONFIGURATION_BUILD_DIR/staging"
# Sorted file list groups similar files together, which improves tar compression
find . \! -type d | rev | sort | rev | tar cjvf "../Sparkle-$CURRENT_PROJECT_VERSION.tar.bz2" --files-from=-
rm -rf "$CONFIGURATION_BUILD_DIR/staging"
fi

View File

@ -1,28 +0,0 @@
#!/bin/sh
set -e
if ! which -s git ; then
exit 0
fi
if [ -z "$SRCROOT" ] || \
[ -z "$BUILT_PRODUCTS_DIR" ] || \
[ -z "$INFOPLIST_PATH" ] || \
[ -z "$CURRENT_PROJECT_VERSION" ]; then
echo "$0: Must be run from Xcode!" 1>&2
exit 1
fi
# Get the current Git master hash
version=$(cd "$SRCROOT" ; git show-ref --abbrev heads/master | awk '{print $1}')
if [ -z "$version" ] ; then
echo "$0: Can't find a Git hash!" 1>&2
exit 0
fi
version="$CURRENT_PROJECT_VERSION git-$version"
# and use it to set the CFBundleShortVersionString value
export PATH="$PATH:/usr/libexec"
PlistBuddy -c "Set :CFBundleShortVersionString '$version'" \
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"

View File

@ -1 +0,0 @@
html

View File

@ -1,20 +0,0 @@
PROJECT_NAME = Sparkle
PROJECT_NUMBER = 1.7.1
PROJECT_BRIEF = "A software update framework for OS X"
PROJECT_LOGO = Resources/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png
RECURSIVE = YES
INPUT = Sparkle
OUTPUT_DIRECTORY = Documentation
GENERATE_HTML = YES
GENERATE_LATEX = NO
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST = YES
GENERATE_TODOLIST = YES
ALPHABETICAL_INDEX = YES
QT_AUTOBRIEF = YES
WARN_IF_UNDOCUMENTED = NO

View File

@ -1,11 +0,0 @@
#!/bin/bash
if [ "$ACTION" = "" ] ; then
if which -s doxygen ; then
doxygen Documentation/Doxyfile
else
echo "warning: Doxygen not found in PATH"
fi
elif [ "$ACTION" = "clean" ] ; then
rm -rf "$SRCROOT/Documentation/html"
fi

View File

@ -1,60 +0,0 @@
Copyright (c) 2006-2013 Andy Matuschak.
Copyright (c) 2009-2013 Elgato Systems GmbH.
Copyright (c) 2011-2014 Kornel Lesiński.
Copyright (c) 2014 C.W. Betts.
Copyright (c) 2014 Petroules Corporation.
Copyright (c) 2014 Big Nerd Ranch.
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=================
EXTERNAL LICENSES
=================
bspatch.c and bsdiff.c, from bsdiff 4.3 <http://www.daemonology.net/bsdiff/>:
Copyright (c) 2003-2005 Colin Percival.
sais.c and sais.c, from sais-lite (2010/08/07) <https://sites.google.com/site/yuta256/sais>:
Copyright (c) 2008-2010 Yuta Mori.
SUDSAVerifier.m:
Copyright (c) 2011 Mark Hamlin.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted providing that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,21 +0,0 @@
.PHONY: all localizable-strings release build test travis
BUILDDIR := $(shell mktemp -d "$(TMPDIR)/Sparkle.XXXXXX")
localizable-strings:
rm -f Sparkle/en.lproj/Sparkle.strings
genstrings -o Sparkle/en.lproj -s SULocalizedString Sparkle/*.m Sparkle/*.h
iconv -f UTF-16 -t UTF-8 < Sparkle/en.lproj/Localizable.strings > Sparkle/en.lproj/Sparkle.strings
rm Sparkle/en.lproj/Localizable.strings
release:
xcodebuild -scheme Distribution -configuration Release -derivedDataPath "$(BUILDDIR)" build
open -R "$(BUILDDIR)/Build/Products/Release/Sparkle-"*.tar.bz2
build:
xcodebuild clean build
test:
xcodebuild -scheme Distribution -configuration Debug test
travis: test

View File

@ -1,46 +0,0 @@
# Sparkle [![Build Status](https://travis-ci.org/sparkle-project/Sparkle.svg?branch=master)](https://travis-ci.org/sparkle-project/Sparkle)
An easy-to-use software update framework for Cocoa developers.
<img src="Resources/Screenshot.png" width="715" alt="Sparkle shows familiar update window with release notes">
## Changes since 1.5b
* Up-to-date with 10.10 SDK and Xcode 6. Supports OS X 10.7+.
* Cleaned up and modernized code, using ARC and Autolayout.
* Merged bugfixes, security fixes and some features from multiple Sparkle forks.
* Truly automatic background updates (no UI at all) when user agreed to "Automatically download and install updates in the future."
* Ability to mark updates as critical.
* Progress and status notifications for the host app.
* Name of finish_installation.app can be configured to match your app's name.
## Features
* True self-updating—the user can choose to automatically download and install all updates.
* Displays a detailed progress window to the user.
* Supports authentication for installing in secure locations.
* Supports Apple code signing and DSA signatures for ultra-secure updates.
* Easy to install. Sparkle requires no code in your app, so it's trivial to upgrade or remove the framework.
* Uses appcasts for release information. Appcasts are supported by 3rd party update-tracking programs and websites.
* Displays release notes to the user via WebKit.
* Sparkle doesn't bug the user until second launch for better first impressions.
* Seamless integration—there's no mention of Sparkle; your icons and app name are used.
* Deep delegate support to make Sparkle work exactly as you need.
* Optionally sends system information to the server when checking for updates.
* Supports bundles, preference panes, plugins, and other non-.app software. Can install .pkg files for more complicated products.
* Supports branches due to minimum OS version requirements.
## Developers
Building Sparkle requires Xcode 5 or above.
### API
Sparkle is built with `-fvisibility=hidden -fvisibility-inlines-hidden` which means no symbols are exported by default.
If you are adding a symbol to the public API you must decorate the declaration with the `SU_EXPORT` macro (grep the source code for examples).
### Building the distribution package
`cd` to the root of the Sparkle source tree and run `make release`. Sparkle-*VERSION*.tar.bz2 will be created in a temporary directory and revealed in Finder after the build has completed.
Alternatively, build the Distribution scheme in the Xcode UI.

View File

@ -1,65 +0,0 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -1,228 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ADP2,1</key>
<string>Developer Transition Kit</string>
<key>iMac1,1</key>
<string>iMac G3 (Rev A-D)</string>
<key>iMac4,1</key>
<string>iMac (Core Duo)</string>
<key>iMac4,2</key>
<string>iMac for Education (17 inch, Core Duo)</string>
<key>iMac5,1</key>
<string>iMac (Core 2 Duo, 17 or 20 inch, SuperDrive)</string>
<key>iMac5,2</key>
<string>iMac (Core 2 Duo, 17 inch, Combo Drive)</string>
<key>iMac6,1</key>
<string>iMac (Core 2 Duo, 24 inch, SuperDrive)</string>
<key>iMac8,1</key>
<string>iMac (Core 2 Duo, 20 or 24 inch, Early 2008 )</string>
<key>iMac9,1</key>
<string>iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 )</string>
<key>iMac10,1</key>
<string>iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 )</string>
<key>iMac11,1</key>
<string>iMac (Core i5 or i7, 27 inch Late 2009)</string>
<key>iMac11,2</key>
<string>iMac (Core i3 or i5, 27 inch Mid 2010)</string>
<key>iMac11,3</key>
<string>iMac (Core i5 or i7, 27 inch Mid 2010)</string>
<key>iMac12,1</key>
<string>iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011)</string>
<key>iMac12,2</key>
<string>iMac (Core i5 or i7, 27 inch Mid 2011)</string>
<key>iMac13,1</key>
<string>iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013)</string>
<key>iMac13,2</key>
<string>iMac (Core i5 or i7, 27 inch Late 2012)</string>
<key>iMac14,1</key>
<string>iMac (Core i5, 21.5 inch Late 2013)</string>
<key>iMac14,2</key>
<string>iMac (Core i5 or i7, 27 inch Late 2013)</string>
<key>iMac14,3</key>
<string>iMac (Core i5 or i7, 21.5 inch Late 2013)</string>
<key>MacBook1,1</key>
<string>MacBook (Core Duo)</string>
<key>MacBook2,1</key>
<string>MacBook (Core 2 Duo)</string>
<key>MacBook4,1</key>
<string>MacBook (Core 2 Duo Feb 2008)</string>
<key>MacBook5,1</key>
<string>MacBook (Core 2 Duo, Late 2008, Unibody)</string>
<key>MacBook5,2</key>
<string>MacBook (Core 2 Duo, Early 2009, White)</string>
<key>MacBook6,1</key>
<string>MacBook (Core 2 Duo, Late 2009, Unibody)</string>
<key>MacBook7,1</key>
<string>MacBook (Core 2 Duo, Mid 2010, White)</string>
<key>MacBookAir1,1</key>
<string>MacBook Air (Core 2 Duo, 13 inch, Early 2008)</string>
<key>MacBookAir2,1</key>
<string>MacBook Air (Core 2 Duo, 13 inch, Mid 2009)</string>
<key>MacBookAir3,1</key>
<string>MacBook Air (Core 2 Duo, 11 inch, Late 2010)</string>
<key>MacBookAir3,2</key>
<string>MacBook Air (Core 2 Duo, 13 inch, Late 2010)</string>
<key>MacBookAir4,1</key>
<string>MacBook Air (Core i5 or i7, 11 inch, Mid 2011)</string>
<key>MacBookAir4,2</key>
<string>MacBook Air (Core i5 or i7, 13 inch, Mid 2011)</string>
<key>MacBookAir5,1</key>
<string>MacBook Air (Core i5 or i7, 11 inch, Mid 2012)</string>
<key>MacBookAir5,2</key>
<string>MacBook Air (Core i5 or i7, 13 inch, Mid 2012)</string>
<key>MacBookAir6,1</key>
<string>MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014)</string>
<key>MacBookAir6,2</key>
<string>MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014)</string>
<key>MacBookPro1,1</key>
<string>MacBook Pro Core Duo (15-inch)</string>
<key>MacBookPro1,2</key>
<string>MacBook Pro Core Duo (17-inch)</string>
<key>MacBookPro2,1</key>
<string>MacBook Pro Core 2 Duo (17-inch)</string>
<key>MacBookPro2,2</key>
<string>MacBook Pro Core 2 Duo (15-inch)</string>
<key>MacBookPro3,1</key>
<string>MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo)</string>
<key>MacBookPro3,2</key>
<string>MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo)</string>
<key>MacBookPro4,1</key>
<string>MacBook Pro (Core 2 Duo Feb 2008)</string>
<key>Macmini1,1</key>
<string>Mac Mini (Core Solo/Duo)</string>
<key>MacPro1,1</key>
<string>Mac Pro (four-core)</string>
<key>MacPro2,1</key>
<string>Mac Pro (eight-core)</string>
<key>MacPro3,1</key>
<string>Mac Pro (January 2008 4- or 8- core &quot;Harpertown&quot;)</string>
<key>MacPro4,1</key>
<string>Mac Pro (March 2009)</string>
<key>MacPro5,1</key>
<string>Mac Pro (August 2010)</string>
<key>PowerBook1,1</key>
<string>PowerBook G3</string>
<key>PowerBook2,1</key>
<string>iBook G3</string>
<key>PowerBook2,2</key>
<string>iBook G3 (FireWire)</string>
<key>PowerBook2,3</key>
<string>iBook G3</string>
<key>PowerBook2,4</key>
<string>iBook G3</string>
<key>PowerBook3,1</key>
<string>PowerBook G3 (FireWire)</string>
<key>PowerBook3,2</key>
<string>PowerBook G4</string>
<key>PowerBook3,3</key>
<string>PowerBook G4 (Gigabit Ethernet)</string>
<key>PowerBook3,4</key>
<string>PowerBook G4 (DVI)</string>
<key>PowerBook3,5</key>
<string>PowerBook G4 (1GHz / 867MHz)</string>
<key>PowerBook4,1</key>
<string>iBook G3 (Dual USB, Late 2001)</string>
<key>PowerBook4,2</key>
<string>iBook G3 (16MB VRAM)</string>
<key>PowerBook4,3</key>
<string>iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003)</string>
<key>PowerBook5,1</key>
<string>PowerBook G4 (17 inch)</string>
<key>PowerBook5,2</key>
<string>PowerBook G4 (15 inch FW 800)</string>
<key>PowerBook5,3</key>
<string>PowerBook G4 (17-inch 1.33GHz)</string>
<key>PowerBook5,4</key>
<string>PowerBook G4 (15 inch 1.5/1.33GHz)</string>
<key>PowerBook5,5</key>
<string>PowerBook G4 (17-inch 1.5GHz)</string>
<key>PowerBook5,6</key>
<string>PowerBook G4 (15 inch 1.67GHz/1.5GHz)</string>
<key>PowerBook5,7</key>
<string>PowerBook G4 (17-inch 1.67GHz)</string>
<key>PowerBook5,8</key>
<string>PowerBook G4 (Double layer SD, 15 inch)</string>
<key>PowerBook5,9</key>
<string>PowerBook G4 (Double layer SD, 17 inch)</string>
<key>PowerBook6,1</key>
<string>PowerBook G4 (12 inch)</string>
<key>PowerBook6,2</key>
<string>PowerBook G4 (12 inch, DVI)</string>
<key>PowerBook6,3</key>
<string>iBook G4</string>
<key>PowerBook6,4</key>
<string>PowerBook G4 (12 inch 1.33GHz)</string>
<key>PowerBook6,5</key>
<string>iBook G4 (Early-Late 2004)</string>
<key>PowerBook6,7</key>
<string>iBook G4 (Mid 2005)</string>
<key>PowerBook6,8</key>
<string>PowerBook G4 (12 inch 1.5GHz)</string>
<key>PowerMac1,1</key>
<string>Power Macintosh G3 (Blue &amp; White)</string>
<key>PowerMac1,2</key>
<string>Power Macintosh G4 (PCI Graphics)</string>
<key>PowerMac10,1</key>
<string>Mac Mini G4</string>
<key>PowerMac10,2</key>
<string>Mac Mini (Late 2005)</string>
<key>PowerMac11,2</key>
<string>Power Macintosh G5 (Late 2005)</string>
<key>PowerMac12,1</key>
<string>iMac G5 (iSight)</string>
<key>PowerMac2,1</key>
<string>iMac G3 (Slot-loading CD-ROM)</string>
<key>PowerMac2,2</key>
<string>iMac G3 (Summer 2000)</string>
<key>PowerMac3,1</key>
<string>Power Macintosh G4 (AGP Graphics)</string>
<key>PowerMac3,2</key>
<string>Power Macintosh G4 (AGP Graphics)</string>
<key>PowerMac3,3</key>
<string>Power Macintosh G4 (Gigabit Ethernet)</string>
<key>PowerMac3,4</key>
<string>Power Macintosh G4 (Digital Audio)</string>
<key>PowerMac3,5</key>
<string>Power Macintosh G4 (Quick Silver)</string>
<key>PowerMac3,6</key>
<string>Power Macintosh G4 (Mirrored Drive Door)</string>
<key>PowerMac4,1</key>
<string>iMac G3 (Early/Summer 2001)</string>
<key>PowerMac4,2</key>
<string>iMac G4 (Flat Panel)</string>
<key>PowerMac4,4</key>
<string>eMac</string>
<key>PowerMac4,5</key>
<string>iMac G4 (17-inch Flat Panel)</string>
<key>PowerMac5,1</key>
<string>Power Macintosh G4 Cube</string>
<key>PowerMac6,1</key>
<string>iMac G4 (USB 2.0)</string>
<key>PowerMac6,3</key>
<string>iMac G4 (20-inch Flat Panel)</string>
<key>PowerMac6,4</key>
<string>eMac (USB 2.0, 2005)</string>
<key>PowerMac7,2</key>
<string>Power Macintosh G5</string>
<key>PowerMac7,3</key>
<string>Power Macintosh G5</string>
<key>PowerMac8,1</key>
<string>iMac G5</string>
<key>PowerMac8,2</key>
<string>iMac G5 (Ambient Light Sensor)</string>
<key>PowerMac9,1</key>
<string>Power Macintosh G5 (Late 2005)</string>
<key>RackMac1,1</key>
<string>Xserve G4</string>
<key>RackMac1,2</key>
<string>Xserve G4 (slot-loading, cluster node)</string>
<key>RackMac3,1</key>
<string>Xserve G5</string>
<key>Xserve1,1</key>
<string>Xserve (Intel Xeon)</string>
<key>Xserve2,1</key>
<string>Xserve (January 2008 quad-core)</string>
</dict>
</plist>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>Your Great App's Changelog</title>
<link>http://you.com/app/appcast.xml</link>
<description>Most recent changes with links to updates.</description>
<language>en</language>
<item>
<title>Version 2.0 (2 bugs fixed; 3 new features)</title>
<sparkle:releaseNotesLink>
http://you.com/app/2.0.html
</sparkle:releaseNotesLink>
<pubDate>Wed, 09 Jan 2006 19:20:11 +0000</pubDate>
<enclosure url="http://you.com/app/Your%20Great%20App%202.0.zip" sparkle:version="2.0" length="1623481" type="application/octet-stream" sparkle:dsaSignature="BAFJW4B6B1K1JyW30nbkBwainOzrN6EQuAh" />
<sparkle:minimumSystemVersion>10.7</sparkle:minimumSystemVersion>
</item>
<item>
<title>Version 1.5 (8 bugs fixed; 2 new features)</title>
<sparkle:releaseNotesLink>
http://you.com/app/1.5.html
</sparkle:releaseNotesLink>
<pubDate>Wed, 01 Jan 2006 12:20:11 +0000</pubDate>
<enclosure url="http://you.com/app/Your%20Great%20App%201.5.zip" sparkle:version="1.5" length="1472893" type="application/octet-stream" sparkle:dsaSignature="234818feCa1JyW30nbkBwainOzrN6EQuAh" />
<sparkle:minimumSystemVersion>10.7</sparkle:minimumSystemVersion>
</item>
<!-- Now here's an example of a version with a weird internal version number (like an SVN revision) but a human-readable external one. -->
<item>
<title>Version 1.4 (5 bugs fixed; 2 new features)</title>
<sparkle:releaseNotesLink>
http://you.com/app/1.4.html
</sparkle:releaseNotesLink>
<pubDate>Wed, 25 Dec 2005 12:20:11 +0000</pubDate>
<enclosure url="http://you.com/app/Your%20Great%20App%201.4.zip" sparkle:version="241" sparkle:shortVersionString="1.4" sparkle:dsaSignature="MC0CFBfeCa1JyW30nbkBwainOzrN6EQuAh=" length="1472349" type="application/octet-stream" />
<sparkle:minimumSystemVersion>10.7</sparkle:minimumSystemVersion>
</item>
</channel>
</rss>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,26 +0,0 @@
Pod::Spec.new do |s|
s.name = "Sparkle"
s.version = "1.9.0"
s.summary = "A software update framework for OS X"
s.description = "Sparkle is an easy-to-use software update framework for Cocoa developers."
s.homepage = "http://sparkle-project.org"
s.license = {
:type => 'MIT',
:file => 'LICENSE'
}
s.authors = {
'Andy Matuschak' => 'andy@andymatuschak.org',
'Kornel Lesiński' => 'pornel@pornel.net',
'C.W. Betts' => 'computers57@hotmail.com',
'Jake Petroules' => 'jake.petroules@petroules.com',
}
s.platform = :osx, '10.7'
s.source = { :http => "https://github.com/sparkle-project/Sparkle/releases/download/#{s.version}/Sparkle-#{s.version}.tar.bz2" }
s.public_header_files = 'Sparkle.framework/Headers/*.h'
s.vendored_framework = 'Sparkle.framework'
s.resources = 'Sparkle.framework'
s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '"${PODS_ROOT}/Sparkle"' }
s.requires_arc = true
end

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Sparkle.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "14732BC51960F69300593899"
BuildableName = "Distribution"
BlueprintName = "Distribution"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "612279D80DB5470200AB99EA"
BuildableName = "Sparkle Unit Tests.xctest"
BlueprintName = "Sparkle Unit Tests"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "14732BC51960F69300593899"
BuildableName = "Distribution"
BlueprintName = "Distribution"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "14732BC51960F69300593899"
BuildableName = "Distribution"
BlueprintName = "Distribution"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "14732BC51960F69300593899"
BuildableName = "Distribution"
BlueprintName = "Distribution"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "Sparkle.framework"
BlueprintName = "Sparkle"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "612279D80DB5470200AB99EA"
BuildableName = "Sparkle Unit Tests.xctest"
BlueprintName = "Sparkle Unit Tests"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "Sparkle.framework"
BlueprintName = "Sparkle"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugXPCServices = "NO"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "Sparkle.framework"
BlueprintName = "Sparkle"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "Sparkle.framework"
BlueprintName = "Sparkle"
ReferencedContainer = "container:Sparkle.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.sparkle-project.Sparkle.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>LSBackgroundOnly</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSUIElement</key>
<string>1</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -1,210 +0,0 @@
#import <AppKit/AppKit.h>
#import "SUInstaller.h"
#import "SUHost.h"
#import "SUStandardVersionComparator.h"
#import "SUStatusController.h"
#import "SUPlainInstallerInternals.h"
#import "SULog.h"
#include <unistd.h>
/*!
* If the Installation takes longer than this time the Application Icon is shown in the Dock so that the user has some feedback.
*/
static const NSTimeInterval SUInstallationTimeLimit = 5;
/*!
* Time this app uses to recheck if the parent has already died.
*/
static const NSTimeInterval SUParentQuitCheckInterval = .25;
@interface TerminationListener : NSObject
@property (copy) NSString *hostpath;
@property (copy) NSString *executablepath;
@property (assign) pid_t parentprocessid;
@property (copy) NSString *folderpath;
@property (copy) NSString *selfPath;
@property (copy) NSString *installationPath;
@property (strong) NSTimer *watchdogTimer;
@property (strong) NSTimer *longInstallationTimer;
@property (strong) SUHost *host;
@property (assign) BOOL shouldRelaunch;
@property (assign) BOOL shouldShowUI;
@property (strong) SUStatusController *statusController;
- (void)parentHasQuit;
- (void)relaunch;
- (void)install;
- (void)showAppIconInDock:(NSTimer *)aTimer;
- (void)watchdog:(NSTimer *)aTimer;
@end
@implementation TerminationListener
@synthesize hostpath;
@synthesize executablepath;
@synthesize parentprocessid;
@synthesize folderpath;
@synthesize selfPath;
@synthesize installationPath;
@synthesize watchdogTimer;
@synthesize longInstallationTimer;
@synthesize host;
@synthesize shouldRelaunch;
@synthesize shouldShowUI;
@synthesize statusController;
- (instancetype)initWithHostPath:(NSString *)inhostpath executablePath:(NSString *)execpath parentProcessId:(pid_t)ppid folderPath:(NSString *)infolderpath shouldRelaunch:(BOOL)relaunch shouldShowUI:(BOOL)showUI selfPath:(NSString *)inSelfPath
{
if (!(self = [super init])) {
return nil;
}
self.hostpath = inhostpath;
self.executablepath = execpath;
self.parentprocessid = ppid;
self.folderpath = infolderpath;
self.selfPath = inSelfPath;
self.shouldRelaunch = relaunch;
self.shouldShowUI = showUI;
BOOL alreadyTerminated = (getppid() == 1); // ppid is launchd (1) => parent terminated already
if (alreadyTerminated)
[self parentHasQuit];
else
self.watchdogTimer = [NSTimer scheduledTimerWithTimeInterval:SUParentQuitCheckInterval target:self selector:@selector(watchdog:) userInfo:nil repeats:YES];
return self;
}
- (void)dealloc
{
[self.longInstallationTimer invalidate];
[self.statusController close];
}
- (void)parentHasQuit
{
[self.watchdogTimer invalidate];
self.longInstallationTimer = [NSTimer scheduledTimerWithTimeInterval:SUInstallationTimeLimit
target: self selector: @selector(showAppIconInDock:)
userInfo:nil repeats:NO];
if (self.folderpath)
[self install];
else
[self relaunch];
}
- (void)watchdog:(NSTimer *)__unused aTimer
{
if (![NSRunningApplication runningApplicationWithProcessIdentifier:self.parentprocessid]) {
[self parentHasQuit];
}
}
- (void)showAppIconInDock:(NSTimer *)__unused aTimer
{
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
}
- (void)relaunch __attribute__((noreturn))
{
if (self.shouldRelaunch)
{
NSString *appPath = nil;
if (!self.folderpath || ![self.executablepath isEqualToString:self.hostpath])
appPath = self.executablepath;
else
appPath = self.installationPath;
[[NSWorkspace sharedWorkspace] openFile:appPath];
}
if (self.folderpath)
{
NSError *theError = nil;
if (![SUPlainInstaller _removeFileAtPath:[SUInstaller updateFolder] error:&theError])
SULog(@"Couldn't remove update folder: %@.", theError);
}
[[NSFileManager defaultManager] removeItemAtPath:self.selfPath error:NULL];
exit(EXIT_SUCCESS);
}
- (void)install
{
NSBundle *theBundle = [NSBundle bundleWithPath:self.hostpath];
self.host = [[SUHost alloc] initWithBundle:theBundle];
self.installationPath = [[self.host installationPath] copy];
if (self.shouldShowUI) {
SUStatusController *statusCtl = [[SUStatusController alloc] initWithHost:self.host]; // We quit anyway after we've installed, so leak this for now.
[statusCtl setButtonTitle:SULocalizedString(@"Cancel Update", @"") target:nil action:Nil isDefault:NO];
[statusCtl beginActionWithTitle:SULocalizedString(@"Installing update...", @"")
maxProgressValue: 0 statusText: @""];
[statusCtl showWindow:self];
[self.statusController close]; // If there's an existing status controller, close it before we release our strong reference to it.
self.statusController = statusCtl; // Keep a strong reference to the status controller, or else it might get prematurely deallocated.
}
[SUInstaller installFromUpdateFolder:self.folderpath
overHost:self.host
installationPath:self.installationPath
versionComparator:[SUStandardVersionComparator defaultComparator]
completionHandler:^(NSError *error) {
if (error) {
if (self.shouldShowUI) {
NSRunAlertPanel(@"", @"%@", @"OK", @"", @"", [error localizedDescription]);
}
exit(EXIT_FAILURE);
} else {
[self relaunch];
}
}];
}
@end
int main(int __unused argc, const char __unused *argv[])
{
@autoreleasepool
{
NSArray *args = [[NSProcessInfo processInfo] arguments];
if (args.count < 5 || args.count > 7) {
return EXIT_FAILURE;
}
BOOL shouldShowUI = (args.count > 6) ? [args[6] boolValue] : YES;
if (shouldShowUI) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
[NSApplication sharedApplication];
TerminationListener *termListen = [[TerminationListener alloc] initWithHostPath:args[1]
executablePath:args[2]
parentProcessId:[args[3] intValue]
folderPath:args[4]
shouldRelaunch:(args.count > 5) ? [args[5] boolValue] : YES
shouldShowUI:shouldShowUI
selfPath:[[NSBundle mainBundle] bundlePath]];
[[NSApplication sharedApplication] run];
// Ensure termListen is not deallocated by ARC before caling -[NSApplication run]
[termListen class];
}
return EXIT_SUCCESS;
}

View File

@ -1,26 +0,0 @@
//
// SUAppcast.h
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUAPPCAST_H
#define SUAPPCAST_H
#import <Foundation/Foundation.h>
#import "SUExport.h"
@class SUAppcastItem;
SU_EXPORT @interface SUAppcast : NSObject<NSURLDownloadDelegate>
@property (copy) NSString *userAgentString;
@property (copy) NSDictionary *httpHeaders;
- (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))err;
@property (readonly, copy) NSArray *items;
@end
#endif

View File

@ -1,281 +0,0 @@
//
// SUAppcast.m
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUAppcast.h"
#import "SUConstants.h"
#import "SULog.h"
@interface NSXMLElement (SUAppcastExtensions)
@property (readonly, copy) NSDictionary *attributesAsDictionary;
@end
@implementation NSXMLElement (SUAppcastExtensions)
- (NSDictionary *)attributesAsDictionary
{
NSEnumerator *attributeEnum = [[self attributes] objectEnumerator];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
for (NSXMLNode *attribute in attributeEnum) {
dictionary[[attribute name]] = [attribute stringValue];
}
return dictionary;
}
@end
@interface SUAppcast () <NSURLDownloadDelegate>
@property (strong) void (^completionBlock)(NSError *);
@property (copy) NSString *downloadFilename;
@property (strong) NSURLDownload *download;
@property (copy) NSArray *items;
- (void)reportError:(NSError *)error;
- (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
@end
@implementation SUAppcast
@synthesize downloadFilename;
@synthesize completionBlock;
@synthesize userAgentString;
@synthesize httpHeaders;
@synthesize download;
@synthesize items;
- (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))block
{
self.completionBlock = block;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
if (self.userAgentString) {
[request setValue:self.userAgentString forHTTPHeaderField:@"User-Agent"];
}
if (self.httpHeaders) {
for (NSString *key in self.httpHeaders) {
id value = [self.httpHeaders objectForKey:key];
[request setValue:value forHTTPHeaderField:key];
}
}
[request setValue:@"application/rss+xml,*/*;q=0.1" forHTTPHeaderField:@"Accept"];
self.download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
}
- (void)download:(NSURLDownload *)__unused aDownload decideDestinationWithSuggestedFilename:(NSString *)filename
{
NSString *destinationFilename = NSTemporaryDirectory();
if (destinationFilename)
{
destinationFilename = [destinationFilename stringByAppendingPathComponent:filename];
[self.download setDestination:destinationFilename allowOverwrite:NO];
}
}
- (void)download:(NSURLDownload *)__unused aDownload didCreateDestination:(NSString *)path
{
self.downloadFilename = path;
}
- (void)downloadDidFinish:(NSURLDownload *)__unused aDownload
{
NSError *error = nil;
NSXMLDocument *document = nil;
BOOL failed = NO;
NSArray *xmlItems = nil;
NSMutableArray *appcastItems = [NSMutableArray array];
if (self.downloadFilename)
{
NSUInteger options = 0;
options = NSXMLNodeLoadExternalEntitiesSameOriginOnly;
document = [[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:self.downloadFilename] options:options error:&error];
[[NSFileManager defaultManager] removeItemAtPath:self.downloadFilename error:nil];
self.downloadFilename = nil;
}
else
{
failed = YES;
}
if (nil == document)
{
failed = YES;
}
else
{
xmlItems = [document nodesForXPath:@"/rss/channel/item" error:&error];
if (nil == xmlItems)
{
failed = YES;
}
}
if (failed == NO)
{
NSEnumerator *nodeEnum = [xmlItems objectEnumerator];
NSXMLNode *node;
NSMutableDictionary *nodesDict = [NSMutableDictionary dictionary];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
while (failed == NO && (node = [nodeEnum nextObject]))
{
// First, we'll "index" all the first-level children of this appcast item so we can pick them out by language later.
if ([[node children] count])
{
node = [node childAtIndex:0];
while (nil != node)
{
NSString *name = [node name];
if (name)
{
NSMutableArray *nodes = nodesDict[name];
if (nodes == nil)
{
nodes = [NSMutableArray array];
nodesDict[name] = nodes;
}
[nodes addObject:node];
}
node = [node nextSibling];
}
}
for (NSString *name in nodesDict)
{
node = [self bestNodeInNodes:nodesDict[name]];
if ([name isEqualToString:SURSSElementEnclosure])
{
// enclosure is flattened as a separate dictionary for some reason
NSDictionary *encDict = [(NSXMLElement *)node attributesAsDictionary];
dict[name] = encDict;
}
else if ([name isEqualToString:SURSSElementPubDate])
{
// pubDate is expected to be an NSDate by SUAppcastItem, but the RSS class was returning an NSString
NSDate *date = [NSDate dateWithNaturalLanguageString:[node stringValue]];
if (date)
dict[name] = date;
}
else if ([name isEqualToString:SUAppcastElementDeltas])
{
NSMutableArray *deltas = [NSMutableArray array];
NSEnumerator *childEnum = [[node children] objectEnumerator];
for (NSXMLNode *child in childEnum) {
if ([[child name] isEqualToString:SURSSElementEnclosure])
[deltas addObject:[(NSXMLElement *)child attributesAsDictionary]];
}
dict[name] = deltas;
}
else if ([name isEqualToString:SUAppcastElementTags]) {
NSMutableArray *tags = [NSMutableArray array];
NSEnumerator *childEnum = [[node children] objectEnumerator];
for (NSXMLNode *child in childEnum) {
[tags addObject:[child name]];
}
dict[name] = tags;
}
else if (name != nil)
{
// add all other values as strings
NSString *theValue = [[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (theValue != nil) {
dict[name] = theValue;
}
}
}
NSString *errString;
SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict failureReason:&errString];
if (anItem)
{
[appcastItems addObject:anItem];
}
else
{
SULog(@"Sparkle Updater: Failed to parse appcast item: %@.\nAppcast dictionary was: %@", errString, dict);
}
[nodesDict removeAllObjects];
[dict removeAllObjects];
}
}
if ([appcastItems count]) {
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
[appcastItems sortUsingDescriptors:@[sort]];
self.items = appcastItems;
}
if (failed) {
[self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while parsing the update feed.", nil) }]];
} else {
self.completionBlock(nil);
self.completionBlock = nil;
}
}
- (void)download:(NSURLDownload *)__unused aDownload didFailWithError:(NSError *)error
{
if (self.downloadFilename) {
[[NSFileManager defaultManager] removeItemAtPath:self.downloadFilename error:nil];
}
self.downloadFilename = nil;
[self reportError:error];
}
- (NSURLRequest *)download:(NSURLDownload *)__unused aDownload willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)__unused redirectResponse
{
return request;
}
- (void)reportError:(NSError *)error
{
NSURL *failingUrl = error.userInfo[NSURLErrorFailingURLErrorKey];
self.completionBlock([NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastError userInfo:@{
NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred in retrieving update information. Please try again later.", nil),
NSLocalizedFailureReasonErrorKey: [error localizedDescription],
NSUnderlyingErrorKey: error,
NSURLErrorFailingURLErrorKey: failingUrl ? failingUrl : [NSNull null],
}]);
self.completionBlock = nil;
}
- (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes
{
// We use this method to pick out the localized version of a node when one's available.
if ([nodes count] == 1)
return nodes[0];
else if ([nodes count] == 0)
return nil;
NSMutableArray *languages = [NSMutableArray array];
NSString *lang;
NSUInteger i;
for (NSXMLElement *node in nodes) {
lang = [[node attributeForName:@"xml:lang"] stringValue];
[languages addObject:(lang ? lang : @"")];
}
lang = [NSBundle preferredLocalizationsFromArray:languages][0];
i = [languages indexOfObject:([languages containsObject:lang] ? lang : @"")];
if (i == NSNotFound) {
i = 0;
}
return nodes[i];
}
@end

View File

@ -1,42 +0,0 @@
//
// SUAppcastItem.h
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUAPPCASTITEM_H
#define SUAPPCASTITEM_H
#include "SUExport.h"
SU_EXPORT @interface SUAppcastItem : NSObject
@property (copy, readonly) NSString *title;
@property (copy, readonly) NSDate *date;
@property (copy, readonly) NSString *itemDescription;
@property (strong, readonly) NSURL *releaseNotesURL;
@property (copy, readonly) NSString *DSASignature;
@property (copy, readonly) NSString *minimumSystemVersion;
@property (copy, readonly) NSString *maximumSystemVersion;
@property (strong, readonly) NSURL *fileURL;
@property (copy, readonly) NSString *versionString;
@property (copy, readonly) NSString *displayVersionString;
@property (copy, readonly) NSDictionary *deltaUpdates;
@property (strong, readonly) NSURL *infoURL;
// Initializes with data from a dictionary provided by the RSS class.
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error;
@property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate;
@property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate;
// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions.
@property (readonly, copy) NSDictionary *propertiesDictionary;
- (NSURL *)infoURL;
@end
#endif

View File

@ -1,180 +0,0 @@
//
// SUAppcastItem.m
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUAppcastItem.h"
#import "SULog.h"
@interface SUAppcastItem ()
@property (copy, readwrite) NSString *title;
@property (copy, readwrite) NSDate *date;
@property (copy, readwrite) NSString *itemDescription;
@property (strong, readwrite) NSURL *releaseNotesURL;
@property (copy, readwrite) NSString *DSASignature;
@property (copy, readwrite) NSString *minimumSystemVersion;
@property (copy, readwrite) NSString *maximumSystemVersion;
@property (strong, readwrite) NSURL *fileURL;
@property (copy, readwrite) NSString *versionString;
@property (copy, readwrite) NSString *displayVersionString;
@property (copy, readwrite) NSDictionary *deltaUpdates;
@property (strong, readwrite) NSURL *infoURL;
@property (readwrite, copy) NSDictionary *propertiesDictionary;
@end
@implementation SUAppcastItem
@synthesize date;
@synthesize deltaUpdates;
@synthesize displayVersionString;
@synthesize DSASignature;
@synthesize fileURL;
@synthesize infoURL;
@synthesize itemDescription;
@synthesize maximumSystemVersion;
@synthesize minimumSystemVersion;
@synthesize releaseNotesURL;
@synthesize title;
@synthesize versionString;
@synthesize propertiesDictionary;
- (BOOL)isDeltaUpdate
{
return self.propertiesDictionary[SURSSElementEnclosure][SUAppcastAttributeDeltaFrom] != nil;
}
- (BOOL)isCriticalUpdate
{
return [self.propertiesDictionary[SUAppcastElementTags] containsObject:SUAppcastElementCriticalUpdate];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict
{
return [self initWithDictionary:dict failureReason:nil];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString *__autoreleasing *)error
{
self = [super init];
if (self) {
id enclosure = dict[SURSSElementEnclosure];
// Try to find a version string.
// Finding the new version number from the RSS feed is a little bit hacky. There are two ways:
// 1. A "sparkle:version" attribute on the enclosure tag, an extension from the RSS spec.
// 2. If there isn't a version attribute, Sparkle will parse the path in the enclosure, expecting
// that it will look like this: http://something.com/YourApp_0.5.zip. It'll read whatever's between the last
// underscore and the last period as the version number. So name your packages like this: APPNAME_VERSION.extension.
// 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[SUAppcastAttributeVersion];
if (newVersion == nil) {
newVersion = dict[SUAppcastAttributeVersion]; // 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?
{
SULog(@"warning: <%@> for URL '%@' is missing %@ attribute. Version comparison may be unreliable. Please always specify %@", SURSSElementEnclosure, enclosure[SURSSAttributeURL], SUAppcastAttributeVersion, SUAppcastAttributeVersion);
// 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.
NSArray *fileComponents = [enclosure[SURSSAttributeURL] componentsSeparatedByString:@"_"];
if ([fileComponents count] > 1) {
newVersion = [[fileComponents lastObject] stringByDeletingPathExtension];
}
}
if (!newVersion) {
if (error) {
*error = [NSString stringWithFormat:@"Feed item lacks %@ attribute, and version couldn't be deduced from file name (would have used last component of a file name like AppName_1.3.4.zip)", SUAppcastAttributeVersion];
}
return nil;
}
propertiesDictionary = [[NSMutableDictionary alloc] initWithDictionary:dict];
self.title = dict[SURSSElementTitle];
self.date = dict[SURSSElementPubDate];
self.itemDescription = dict[SURSSElementDescription];
NSString *theInfoURL = dict[SURSSElementLink];
if (theInfoURL) {
if (![theInfoURL isKindOfClass:[NSString class]]) {
SULog(@"%@ -%@ Info URL is not of valid type.", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
} else {
self.infoURL = [NSURL URLWithString:theInfoURL];
}
}
// Need an info URL or an enclosure URL. Former to show "More Info"
// page, latter to download & install:
if (!enclosure && !theInfoURL) {
if (error) {
*error = @"No enclosure in feed item";
}
return nil;
}
NSString *enclosureURLString = enclosure[SURSSAttributeURL];
if (!enclosureURLString && !theInfoURL) {
if (error) {
*error = @"Feed item's enclosure lacks URL";
}
return nil;
}
if (enclosureURLString) {
NSString *fileURLString = [[enclosureURLString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
self.fileURL = [NSURL URLWithString:fileURLString];
}
if (enclosure) {
self.DSASignature = enclosure[SUAppcastAttributeDSASignature];
}
self.versionString = newVersion;
self.minimumSystemVersion = dict[SUAppcastElementMinimumSystemVersion];
self.maximumSystemVersion = dict[SUAppcastElementMaximumSystemVersion];
NSString *shortVersionString = enclosure[SUAppcastAttributeShortVersionString];
if (nil == shortVersionString) {
shortVersionString = dict[SUAppcastAttributeShortVersionString]; // fall back on the <item>
}
if (shortVersionString) {
self.displayVersionString = shortVersionString;
} else {
self.displayVersionString = self.versionString;
}
// Find the appropriate release notes URL.
if (dict[SUAppcastElementReleaseNotesLink]) {
self.releaseNotesURL = [NSURL URLWithString:dict[SUAppcastElementReleaseNotesLink]];
} else if ([self.itemDescription hasPrefix:@"http://"] || [self.itemDescription hasPrefix:@"https://"]) { // if the description starts with http:// or https:// use that.
self.releaseNotesURL = [NSURL URLWithString:self.itemDescription];
} else {
self.releaseNotesURL = nil;
}
NSArray *deltaDictionaries = dict[SUAppcastElementDeltas];
if (deltaDictionaries) {
NSMutableDictionary *deltas = [NSMutableDictionary dictionary];
for (NSDictionary *deltaDictionary in deltaDictionaries) {
NSMutableDictionary *fakeAppCastDict = [dict mutableCopy];
[fakeAppCastDict removeObjectForKey:SUAppcastElementDeltas];
fakeAppCastDict[SURSSElementEnclosure] = deltaDictionary;
SUAppcastItem *deltaItem = [[[self class] alloc] initWithDictionary:fakeAppCastDict];
deltas[deltaDictionary[SUAppcastAttributeDeltaFrom]] = deltaItem;
}
self.deltaUpdates = deltas;
}
}
return self;
}
@end

View File

@ -1,30 +0,0 @@
//
// SUAutomaticUpdateAlert.h
// Sparkle
//
// Created by Andy Matuschak on 3/18/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUAUTOMATICUPDATEALERT_H
#define SUAUTOMATICUPDATEALERT_H
#import "SUWindowController.h"
typedef NS_ENUM(NSInteger, SUAutomaticInstallationChoice) {
SUInstallNowChoice,
SUInstallLaterChoice,
SUDoNotInstallChoice
};
@class SUAppcastItem, SUHost;
@interface SUAutomaticUpdateAlert : SUWindowController
- (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)hostBundle completionBlock:(void (^)(SUAutomaticInstallationChoice))c;
- (IBAction)installNow:sender;
- (IBAction)installLater:sender;
- (IBAction)doNotInstall:sender;
@end
#endif

View File

@ -1,89 +0,0 @@
//
// SUAutomaticUpdateAlert.m
// Sparkle
//
// Created by Andy Matuschak on 3/18/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#import "SUAutomaticUpdateAlert.h"
#import "SUHost.h"
@interface SUAutomaticUpdateAlert ()
@property (strong) void(^completionBlock)(SUAutomaticInstallationChoice);
@property (strong) SUAppcastItem *updateItem;
@property (strong) SUHost *host;
@end
@implementation SUAutomaticUpdateAlert
@synthesize host;
@synthesize updateItem;
@synthesize completionBlock;
- (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)aHost completionBlock:(void (^)(SUAutomaticInstallationChoice))block
{
self = [super initWithWindowNibName:@"SUAutomaticUpdateAlert"];
if (self) {
self.updateItem = item;
self.completionBlock = block;
self.host = aHost;
[self setShouldCascadeWindows:NO];
[[self window] center];
}
return self;
}
- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self.host bundlePath], [self.host installationPath]]; }
- (IBAction)installNow:(id)__unused sender
{
[self close];
self.completionBlock(SUInstallNowChoice);
self.completionBlock = nil;
}
- (IBAction)installLater:(id)__unused sender
{
[self close];
self.completionBlock(SUInstallLaterChoice);
self.completionBlock = nil;
}
- (IBAction)doNotInstall:(id)__unused sender
{
[self close];
self.completionBlock(SUDoNotInstallChoice);
self.completionBlock = nil;
}
- (NSImage *)applicationIcon
{
return [self.host icon];
}
- (NSString *)titleText
{
if ([self.updateItem isCriticalUpdate])
{
return [NSString stringWithFormat:SULocalizedString(@"An important update to %@ is ready to install", nil), [self.host name]];
}
else
{
return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is ready to install!", nil), [self.host name]];
}
}
- (NSString *)descriptionText
{
if ([self.updateItem isCriticalUpdate])
{
return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?", nil), [self.host name], [self.updateItem displayVersionString]];
}
else
{
return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?", nil), [self.host name], [self.updateItem displayVersionString]];
}
}
@end

View File

@ -1,20 +0,0 @@
//
// SUAutomaticUpdateDriver.h
// Sparkle
//
// Created by Andy Matuschak on 5/6/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUAUTOMATICUPDATEDRIVER_H
#define SUAUTOMATICUPDATEDRIVER_H
#import <Cocoa/Cocoa.h>
#import "SUBasicUpdateDriver.h"
#import "SUAutomaticUpdateAlert.h"
@interface SUAutomaticUpdateDriver : SUBasicUpdateDriver <SUUnarchiverDelegate>
@end
#endif

View File

@ -1,196 +0,0 @@
//
// SUAutomaticUpdateDriver.m
// Sparkle
//
// Created by Andy Matuschak on 5/6/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUAutomaticUpdateDriver.h"
#import "SUAutomaticUpdateAlert.h"
#import "SUHost.h"
#import "SUConstants.h"
// If the user hasn't quit in a week, ask them if they want to relaunch to get the latest bits. It doesn't matter that this measure of "one day" is imprecise.
static const NSTimeInterval SUAutomaticUpdatePromptImpatienceTimer = 60 * 60 * 24 * 7;
@interface SUUpdateDriver ()
@property (getter=isInterruptible) BOOL interruptible;
@end
@interface SUAutomaticUpdateDriver ()
@property (assign) BOOL postponingInstallation;
@property (assign) BOOL showErrors;
@property (assign) BOOL willUpdateOnTermination;
@property (strong) SUAutomaticUpdateAlert *alert;
@property (strong) NSTimer *showUpdateAlertTimer;
@end
@implementation SUAutomaticUpdateDriver
@synthesize postponingInstallation;
@synthesize showErrors;
@synthesize willUpdateOnTermination;
@synthesize alert;
@synthesize showUpdateAlertTimer;
- (void)showUpdateAlert
{
self.interruptible = NO;
self.alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUAutomaticInstallationChoice choice) {
[self automaticUpdateAlertFinishedWithChoice:choice];
}];
// If the app is a menubar app or the like, we need to focus it first and alter the
// update prompt to behave like a normal window. Otherwise if the window were hidden
// there may be no way for the application to be activated to make it visible again.
if ([self.host isBackgroundApplication]) {
[[self.alert window] setHidesOnDeactivate:NO];
[NSApp activateIgnoringOtherApps:YES];
}
if ([NSApp isActive])
[[self.alert window] makeKeyAndOrderFront:self];
else
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp];
}
- (void)unarchiverDidFinish:(SUUnarchiver *)__unused ua
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];
// Sudden termination is available on 10.6+
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
[processInfo disableSuddenTermination];
self.willUpdateOnTermination = YES;
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:willInstallUpdateOnQuit:immediateInstallationInvocation:)])
{
BOOL relaunch = YES;
BOOL showUI = NO;
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(installWithToolAndRelaunch:displayingUserInterface:)]];
[invocation setSelector:@selector(installWithToolAndRelaunch:displayingUserInterface:)];
[invocation setArgument:&relaunch atIndex:2];
[invocation setArgument:&showUI atIndex:3];
[invocation setTarget:self];
[updaterDelegate updater:self.updater willInstallUpdateOnQuit:self.updateItem immediateInstallationInvocation:invocation];
}
// If this is marked as a critical update, we'll prompt the user to install it right away.
if ([self.updateItem isCriticalUpdate])
{
[self showUpdateAlert];
}
else
{
self.showUpdateAlertTimer = [NSTimer scheduledTimerWithTimeInterval:SUAutomaticUpdatePromptImpatienceTimer target:self selector:@selector(showUpdateAlert) userInfo:nil repeats:NO];
// At this point the driver is idle, allow it to be interrupted for user-initiated update checks.
self.interruptible = YES;
}
}
- (void)stopUpdatingOnTermination
{
if (self.willUpdateOnTermination)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
[processInfo enableSuddenTermination];
self.willUpdateOnTermination = NO;
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:didCancelInstallUpdateOnQuit:)])
[updaterDelegate updater:self.updater didCancelInstallUpdateOnQuit:self.updateItem];
}
}
- (void)invalidateShowUpdateAlertTimer
{
[self.showUpdateAlertTimer invalidate];
self.showUpdateAlertTimer = nil;
}
- (void)dealloc
{
[self stopUpdatingOnTermination];
[self invalidateShowUpdateAlertTimer];
}
- (void)abortUpdate
{
[self stopUpdatingOnTermination];
[self invalidateShowUpdateAlertTimer];
[super abortUpdate];
}
- (void)applicationDidBecomeActive:(NSNotification *)__unused aNotification
{
[[self.alert window] makeKeyAndOrderFront:self];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:NSApp];
}
- (void)automaticUpdateAlertFinishedWithChoice:(SUAutomaticInstallationChoice)choice
{
switch (choice)
{
case SUInstallNowChoice:
[self stopUpdatingOnTermination];
[self installWithToolAndRelaunch:YES];
break;
case SUInstallLaterChoice:
self.postponingInstallation = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];
// We're already waiting on quit, just indicate that we're idle.
self.interruptible = YES;
break;
case SUDoNotInstallChoice:
[self.host setObject:[self.updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey];
[self abortUpdate];
break;
}
}
- (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI
{
if (relaunch) {
[self stopUpdatingOnTermination];
}
self.showErrors = YES;
[super installWithToolAndRelaunch:relaunch displayingUserInterface:showUI];
}
- (void)applicationWillTerminate:(NSNotification *)__unused note
{
[self installWithToolAndRelaunch:NO];
}
- (void)abortUpdateWithError:(NSError *)error
{
if (self.showErrors) {
[super abortUpdateWithError:error];
} else {
// Call delegate separately here because otherwise it won't know we stopped.
// Normally this gets called by the superclass
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) {
[updaterDelegate updater:self.updater didAbortWithError:error];
}
[self abortUpdate];
}
}
@end

View File

@ -1,56 +0,0 @@
//
// SUBasicUpdateDriver.h
// Sparkle,
//
// Created by Andy Matuschak on 4/23/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUBASICUPDATEDRIVER_H
#define SUBASICUPDATEDRIVER_H
#import <Cocoa/Cocoa.h>
#import "SUUpdateDriver.h"
#import "SUUnarchiver.h"
#import "SUAppcast.h"
@class SUAppcastItem, SUHost;
@interface SUBasicUpdateDriver : SUUpdateDriver <NSURLDownloadDelegate, SUUnarchiverDelegate>
@property (strong, readonly) SUAppcastItem *updateItem;
@property (strong, readonly) NSURLDownload *download;
@property (copy, readonly) NSString *downloadPath;
- (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)host;
- (BOOL)isItemNewer:(SUAppcastItem *)ui;
- (BOOL)hostSupportsItem:(SUAppcastItem *)ui;
- (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui;
- (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui;
- (void)appcastDidFinishLoading:(SUAppcast *)ac;
- (void)didFindValidUpdate;
- (void)didNotFindUpdate;
- (void)downloadUpdate;
- (void)download:(NSURLDownload *)d decideDestinationWithSuggestedFilename:(NSString *)name;
- (void)downloadDidFinish:(NSURLDownload *)d;
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error;
- (void)extractUpdate;
- (void)unarchiverDidFinish:(SUUnarchiver *)ua;
- (void)unarchiverDidFail:(SUUnarchiver *)ua;
- (void)failedToApplyDeltaUpdate;
- (void)installWithToolAndRelaunch:(BOOL)relaunch;
- (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI;
- (void)installerForHost:(SUHost *)host failedWithError:(NSError *)error;
- (void)cleanUpDownload;
- (void)abortUpdate;
- (void)abortUpdateWithError:(NSError *)error;
- (void)terminateApp;
@end
#endif

View File

@ -1,465 +0,0 @@
//
// SUBasicUpdateDriver.m
// Sparkle
//
// Created by Andy Matuschak on 4/23/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUBasicUpdateDriver.h"
#import "SUHost.h"
#import "SUDSAVerifier.h"
#import "SUInstaller.h"
#import "SUStandardVersionComparator.h"
#import "SUUnarchiver.h"
#import "SUConstants.h"
#import "SULog.h"
#import "SUPlainInstaller.h"
#import "SUPlainInstallerInternals.h"
#import "SUBinaryDeltaCommon.h"
#import "SUCodeSigningVerifier.h"
#import "SUUpdater_Private.h"
@interface SUBasicUpdateDriver ()
@property (strong) SUAppcastItem *updateItem;
@property (strong) NSURLDownload *download;
@property (copy) NSString *downloadPath;
@property (strong) SUAppcastItem *nonDeltaUpdateItem;
@property (copy) NSString *tempDir;
@property (copy) NSString *relaunchPath;
@end
@implementation SUBasicUpdateDriver
@synthesize updateItem;
@synthesize download;
@synthesize downloadPath;
@synthesize nonDeltaUpdateItem;
@synthesize tempDir;
@synthesize relaunchPath;
- (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)aHost
{
[super checkForUpdatesAtURL:URL host:aHost];
if ([aHost isRunningOnReadOnlyVolume])
{
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURunningFromDiskImageError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again.", nil), [aHost name]] }]];
return;
}
SUAppcast *appcast = [[SUAppcast alloc] init];
[appcast setUserAgentString:[self.updater userAgentString]];
[appcast setHttpHeaders:[self.updater httpHeaders]];
[appcast fetchAppcastFromURL:URL completionBlock:^(NSError *error) {
if (error) {
[self abortUpdateWithError:error];
} else {
[self appcastDidFinishLoading:appcast];
}
}];
}
- (id<SUVersionComparison>)versionComparator
{
id<SUVersionComparison> comparator = nil;
// Give the delegate a chance to provide a custom version comparator
if ([[self.updater delegate] respondsToSelector:@selector(versionComparatorForUpdater:)]) {
comparator = [[self.updater delegate] versionComparatorForUpdater:self.updater];
}
// If we don't get a comparator from the delegate, use the default comparator
if (!comparator) {
comparator = [SUStandardVersionComparator defaultComparator];
}
return comparator;
}
- (BOOL)isItemNewer:(SUAppcastItem *)ui
{
return [[self versionComparator] compareVersion:[self.host version] toVersion:[ui versionString]] == NSOrderedAscending;
}
- (BOOL)hostSupportsItem:(SUAppcastItem *)ui
{
if (([ui minimumSystemVersion] == nil || [[ui minimumSystemVersion] isEqualToString:@""]) &&
([ui maximumSystemVersion] == nil || [[ui maximumSystemVersion] isEqualToString:@""])) { return YES; }
BOOL minimumVersionOK = TRUE;
BOOL maximumVersionOK = TRUE;
// Check minimum and maximum System Version
if ([ui minimumSystemVersion] != nil && ![[ui minimumSystemVersion] isEqualToString:@""]) {
minimumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui minimumSystemVersion] toVersion:[SUHost systemVersionString]] != NSOrderedDescending;
}
if ([ui maximumSystemVersion] != nil && ![[ui maximumSystemVersion] isEqualToString:@""]) {
maximumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui maximumSystemVersion] toVersion:[SUHost systemVersionString]] != NSOrderedAscending;
}
return minimumVersionOK && maximumVersionOK;
}
- (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui
{
NSString *skippedVersion = [self.host objectForUserDefaultsKey:SUSkippedVersionKey];
if (skippedVersion == nil) { return NO; }
return [[self versionComparator] compareVersion:[ui versionString] toVersion:skippedVersion] != NSOrderedDescending;
}
- (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui
{
return ui && [self hostSupportsItem:ui] && [self isItemNewer:ui] && ![self itemContainsSkippedVersion:ui];
}
- (void)appcastDidFinishLoading:(SUAppcast *)ac
{
if ([[self.updater delegate] respondsToSelector:@selector(updater:didFinishLoadingAppcast:)]) {
[[self.updater delegate] updater:self.updater didFinishLoadingAppcast:ac];
}
NSDictionary *userInfo = (ac != nil) ? @{ SUUpdaterAppcastNotificationKey: ac } : nil;
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFinishLoadingAppCastNotification object:self.updater userInfo:userInfo];
SUAppcastItem *item = nil;
// Now we have to find the best valid update in the appcast.
if ([[self.updater delegate] respondsToSelector:@selector(bestValidUpdateInAppcast:forUpdater:)]) // Does the delegate want to handle it?
{
item = [[self.updater delegate] bestValidUpdateInAppcast:ac forUpdater:self.updater];
}
else // If not, we'll take care of it ourselves.
{
// Find the first update we can actually use.
NSEnumerator *updateEnumerator = [[ac items] objectEnumerator];
do {
item = [updateEnumerator nextObject];
} while (item && ![self hostSupportsItem:item]);
SUAppcastItem *deltaUpdateItem = [item deltaUpdates][[self.host version]];
if (deltaUpdateItem && [self hostSupportsItem:deltaUpdateItem]) {
self.nonDeltaUpdateItem = item;
item = deltaUpdateItem;
}
}
if ([self itemContainsValidUpdate:item]) {
self.updateItem = item;
[self didFindValidUpdate];
} else {
self.updateItem = nil;
[self didNotFindUpdate];
}
}
- (void)didFindValidUpdate
{
assert(self.updateItem);
if ([[self.updater delegate] respondsToSelector:@selector(updater:didFindValidUpdate:)]) {
[[self.updater delegate] updater:self.updater didFindValidUpdate:self.updateItem];
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFindValidUpdateNotification
object:self.updater
userInfo:@{ SUUpdaterAppcastItemNotificationKey: self.updateItem }];
[self downloadUpdate];
}
- (void)didNotFindUpdate
{
if ([[self.updater delegate] respondsToSelector:@selector(updaterDidNotFindUpdate:)]) {
[[self.updater delegate] updaterDidNotFindUpdate:self.updater];
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater];
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain
code:SUNoUpdateError
userInfo:@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"You already have the newest version of %@.", "'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI)"), self.host.name]
}]];
}
- (void)downloadUpdate
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self.updateItem fileURL]];
[request setValue:[self.updater userAgentString] forHTTPHeaderField:@"User-Agent"];
self.download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
}
- (void)download:(NSURLDownload *)__unused d decideDestinationWithSuggestedFilename:(NSString *)name
{
NSString *downloadFileName = [NSString stringWithFormat:@"%@ %@", [self.host name], [self.updateItem versionString]];
self.tempDir = [self.host.appCachePath stringByAppendingPathComponent:downloadFileName];
int cnt = 1;
while ([[NSFileManager defaultManager] fileExistsAtPath:self.tempDir] && cnt <= 999)
{
self.tempDir = [self.host.appCachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@ %d", downloadFileName, cnt++]];
}
// Create the temporary directory if necessary.
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:self.tempDir withIntermediateDirectories:YES attributes:nil error:NULL];
if (!success)
{
// Okay, something's really broken with this user's file structure.
[self.download cancel];
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUTemporaryDirectoryError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Can't make a temporary directory for the update download at %@.", self.tempDir] }]];
}
self.downloadPath = [self.tempDir stringByAppendingPathComponent:name];
[self.download setDestination:self.downloadPath allowOverwrite:YES];
}
- (BOOL)validateUpdateDownloadedToPath:(NSString *)downloadedPath extractedToPath:(NSString *)extractedPath DSASignature:(NSString *)DSASignature publicDSAKey:(NSString *)publicDSAKey
{
NSString *newBundlePath = [SUInstaller appPathInUpdateFolder:extractedPath forHost:self.host];
if (newBundlePath) {
if ([SUCodeSigningVerifier hostApplicationIsCodeSigned]) {
NSError *error = nil;
if ([SUCodeSigningVerifier codeSignatureMatchesHostAndIsValidAtPath:newBundlePath error:&error]) {
return YES;
} else {
SULog(@"Code signature check on update failed: %@. Sparkle will use DSA signature instead.", error);
}
} else {
SULog(@"The host app is not signed using Apple Code Signing, and therefore cannot verify updates this way. Sparkle will use DSA signature instead.");
}
}
if (DSASignature) {
return [SUDSAVerifier validatePath:downloadedPath withEncodedDSASignature:DSASignature withPublicDSAKey:publicDSAKey];
} else {
SULog(@"The appcast item for the update has no DSA signature. The update will be rejected, because both DSA and Apple Code Signing verification failed.");
return NO;
}
}
- (void)downloadDidFinish:(NSURLDownload *)__unused d
{
assert(self.updateItem);
[self extractUpdate];
}
- (void)download:(NSURLDownload *)__unused download didFailWithError:(NSError *)error
{
NSURL *failingUrl = error.userInfo[NSURLErrorFailingURLErrorKey];
if (!failingUrl) {
failingUrl = [self.updateItem fileURL];
}
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:@{
NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while downloading the update. Please try again later.", nil),
NSUnderlyingErrorKey: error,
NSURLErrorFailingURLErrorKey: failingUrl ? failingUrl : [NSNull null],
}]];
}
- (BOOL)download:(NSURLDownload *)__unused download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType
{
// We don't want the download system to extract our gzips.
// Note that we use a substring matching here instead of direct comparison because the docs say "application/gzip" but the system *uses* "application/x-gzip". This is a documentation bug.
return ([encodingType rangeOfString:@"gzip"].location == NSNotFound);
}
- (void)extractUpdate
{
SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:self.downloadPath updatingHostBundlePath:[[self.host bundle] bundlePath]];
if (!unarchiver) {
SULog(@"Error: No valid unarchiver for %@!", self.downloadPath);
[self unarchiverDidFail:nil];
return;
}
unarchiver.delegate = self;
[unarchiver start];
}
- (void)failedToApplyDeltaUpdate
{
// When a delta update fails to apply we fall back on updating via a full install.
self.updateItem = self.nonDeltaUpdateItem;
self.nonDeltaUpdateItem = nil;
[self downloadUpdate];
}
- (void)unarchiverDidFinish:(SUUnarchiver *)__unused ua
{
assert(self.updateItem);
[self installWithToolAndRelaunch:YES];
}
- (void)unarchiverDidFail:(SUUnarchiver *)__unused ua
{
if ([self.updateItem isDeltaUpdate]) {
[self failedToApplyDeltaUpdate];
return;
}
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil) }]];
}
- (void)installWithToolAndRelaunch:(BOOL)relaunch
{
// Perhaps a poor assumption but: if we're not relaunching, we assume we shouldn't be showing any UI either. Because non-relaunching installations are kicked off without any user interaction, we shouldn't be interrupting them.
[self installWithToolAndRelaunch:relaunch displayingUserInterface:relaunch];
}
- (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI
{
assert(self.updateItem);
if (![self validateUpdateDownloadedToPath:self.downloadPath extractedToPath:self.tempDir DSASignature:self.updateItem.DSASignature publicDSAKey:self.host.publicDSAKey])
{
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil),
NSLocalizedFailureReasonErrorKey: SULocalizedString(@"The update is improperly signed.", nil),
};
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUSignatureError userInfo:userInfo]];
return;
}
if (![self.updater mayUpdateAndRestart])
{
[self abortUpdate];
return;
}
// Give the host app an opportunity to postpone the install and relaunch.
static BOOL postponedOnce = NO;
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if (!postponedOnce && [updaterDelegate respondsToSelector:@selector(updater:shouldPostponeRelaunchForUpdate:untilInvoking:)])
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(installWithToolAndRelaunch:)]];
[invocation setSelector:@selector(installWithToolAndRelaunch:)];
[invocation setArgument:&relaunch atIndex:2];
[invocation setTarget:self];
postponedOnce = YES;
if ([updaterDelegate updater:self.updater shouldPostponeRelaunchForUpdate:self.updateItem untilInvoking:invocation]) {
return;
}
}
if ([updaterDelegate respondsToSelector:@selector(updater:willInstallUpdate:)]) {
[updaterDelegate updater:self.updater willInstallUpdate:self.updateItem];
}
NSBundle *sparkleBundle = [NSBundle bundleWithIdentifier:SUBundleIdentifier];
// Copy the relauncher into a temporary directory so we can get to it after the new version's installed.
// Only the paranoid survive: if there's already a stray copy of relaunch there, we would have problems.
NSString *const relaunchPathToCopy = [sparkleBundle pathForResource:[[sparkleBundle infoDictionary] objectForKey:SURelaunchToolNameKey] ofType:@"app"];
if (relaunchPathToCopy != nil) {
NSString *targetPath = [self.host.appCachePath stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]];
// Only the paranoid survive: if there's already a stray copy of relaunch there, we would have problems.
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:[targetPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:@{} error:&error];
if ([SUPlainInstaller copyPathWithAuthentication:relaunchPathToCopy overPath:targetPath temporaryName:nil error:&error]) {
self.relaunchPath = targetPath;
} else {
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:@{
NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil),
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Couldn't copy relauncher (%@) to temporary path (%@)! %@", relaunchPathToCopy, targetPath, (error ? [error localizedDescription] : @"")]
}]];
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self];
if ([updaterDelegate respondsToSelector:@selector(updaterWillRelaunchApplication:)])
[updaterDelegate updaterWillRelaunchApplication:self.updater];
if (!self.relaunchPath || ![[NSFileManager defaultManager] fileExistsAtPath:self.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:@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [self.host name]],
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", self.relaunchPath]
}]];
// We intentionally don't abandon the update here so that the host won't initiate another.
return;
}
NSString *pathToRelaunch = [self.host bundlePath];
if ([updaterDelegate respondsToSelector:@selector(pathToRelaunchForUpdater:)]) {
pathToRelaunch = [updaterDelegate pathToRelaunchForUpdater:self.updater];
}
NSString *relaunchToolPath = [[NSBundle bundleWithPath:self.relaunchPath] executablePath];
[NSTask launchedTaskWithLaunchPath:relaunchToolPath arguments:@[[self.host bundlePath],
pathToRelaunch,
[NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]],
self.tempDir,
relaunch ? @"1" : @"0",
showUI ? @"1" : @"0"]];
[self terminateApp];
}
- (void)terminateApp
{
[NSApp terminate:self];
}
- (void)cleanUpDownload
{
if (self.tempDir != nil) // tempDir contains downloadPath, so we implicitly delete both here.
{
BOOL success = NO;
NSError *error = nil;
success = [[NSFileManager defaultManager] removeItemAtPath:self.tempDir error:&error]; // Clean up the copied relauncher
if (!success)
[[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[self.tempDir stringByDeletingLastPathComponent] destination:@"" files:@[[self.tempDir lastPathComponent]] tag:NULL];
}
}
- (void)installerForHost:(SUHost *)aHost failedWithError:(NSError *)error
{
if (aHost != self.host) {
return;
}
NSError *dontThrow = nil;
[[NSFileManager defaultManager] removeItemAtPath:self.relaunchPath error:&dontThrow]; // Clean up the copied relauncher
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:@{
NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while installing the update. Please try again later.", nil),
NSLocalizedFailureReasonErrorKey: [error localizedDescription],
NSUnderlyingErrorKey: error,
}]];
}
- (void)abortUpdate
{
[self cleanUpDownload];
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.updateItem = nil;
[super abortUpdate];
}
- (void)abortUpdateWithError:(NSError *)error
{
if ([error code] != SUNoUpdateError) { // Let's not bother logging this.
SULog(@"Error: %@ %@ (URL %@)", error.localizedDescription, error.localizedFailureReason, error.userInfo[NSURLErrorFailingURLErrorKey]);
}
if (self.download) {
[self.download cancel];
}
// Notify host app that update has aborted
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) {
[updaterDelegate updater:self.updater didAbortWithError:error];
}
[self abortUpdate];
}
@end

View File

@ -1,15 +0,0 @@
//
// SUBinaryDeltaApply.h
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#ifndef SUBINARYDELTAAPPLY_H
#define SUBINARYDELTAAPPLY_H
@class NSString;
int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, BOOL verbose);
#endif

View File

@ -1,226 +0,0 @@
//
// SUBinaryDeltaApply.m
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#import "SUBinaryDeltaApply.h"
#import "SUBinaryDeltaCommon.h"
#include <CommonCrypto/CommonDigest.h>
#import <Foundation/Foundation.h>
#include "bspatch.h"
#include <stdio.h>
#include <stdlib.h>
#include <xar/xar.h>
static BOOL applyBinaryDeltaToFile(xar_t x, xar_file_t file, NSString *sourceFilePath, NSString *destinationFilePath)
{
NSString *patchFile = temporaryFilename(@"apply-binary-delta");
xar_extract_tofile(x, file, [patchFile fileSystemRepresentation]);
const char *argv[] = {"/usr/bin/bspatch", [sourceFilePath fileSystemRepresentation], [destinationFilePath fileSystemRepresentation], [patchFile fileSystemRepresentation]};
BOOL success = (bspatch(4, (char **)argv) == 0);
unlink([patchFile fileSystemRepresentation]);
return success;
}
int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, BOOL verbose)
{
xar_t x = xar_open([patchFile fileSystemRepresentation], READ);
if (!x) {
fprintf(stderr, "Unable to open %s. Giving up.\n", [patchFile fileSystemRepresentation]);
return 1;
}
SUBinaryDeltaMajorVersion majorDiffVersion = FIRST_DELTA_DIFF_MAJOR_VERSION;
SUBinaryDeltaMinorVersion minorDiffVersion = FIRST_DELTA_DIFF_MINOR_VERSION;
NSString *expectedBeforeHashv1 = nil;
NSString *expectedAfterHashv1 = nil;
NSString *expectedNewBeforeHash = nil;
NSString *expectedNewAfterHash = nil;
xar_subdoc_t subdoc;
for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) {
if (!strcmp(xar_subdoc_name(subdoc), BINARY_DELTA_ATTRIBUTES_KEY)) {
const char *value = 0;
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, MAJOR_DIFF_VERSION_KEY, &value);
if (value)
majorDiffVersion = (SUBinaryDeltaMajorVersion)[@(value) intValue];
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, MINOR_DIFF_VERSION_KEY, &value);
if (value)
minorDiffVersion = (SUBinaryDeltaMinorVersion)[@(value) intValue];
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, BEFORE_TREE_SHA1_KEY, &value);
if (value)
expectedNewBeforeHash = @(value);
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, AFTER_TREE_SHA1_KEY, &value);
if (value)
expectedNewAfterHash = @(value);
// only available in version 1.0
xar_subdoc_prop_get(subdoc, BEFORE_TREE_SHA1_OLD_KEY, &value);
if (value)
expectedBeforeHashv1 = @(value);
// only available in version 1.0
xar_subdoc_prop_get(subdoc, AFTER_TREE_SHA1_OLD_KEY, &value);
if (value)
expectedAfterHashv1 = @(value);
}
}
if (majorDiffVersion < FIRST_DELTA_DIFF_MAJOR_VERSION) {
fprintf(stderr, "Unable to identify diff-version %u in delta. Giving up.\n", majorDiffVersion);
return 1;
}
if (majorDiffVersion > LATEST_DELTA_DIFF_MAJOR_VERSION) {
fprintf(stderr, "A later version is needed to apply this patch (on major version %u, but patch requests version %u).\n", LATEST_DELTA_DIFF_MAJOR_VERSION, majorDiffVersion);
return 1;
}
BOOL usesNewTreeHash = MAJOR_VERSION_IS_AT_LEAST(majorDiffVersion, SUBeigeMajorVersion);
NSString *expectedBeforeHash = usesNewTreeHash ? expectedNewBeforeHash : expectedBeforeHashv1;
NSString *expectedAfterHash = usesNewTreeHash ? expectedNewAfterHash : expectedAfterHashv1;
if (!expectedBeforeHash || !expectedAfterHash) {
fprintf(stderr, "Unable to find before-sha1 or after-sha1 metadata in delta. Giving up.\n");
return 1;
}
if (verbose) {
fprintf(stderr, "Applying version %u.%u patch...\n", majorDiffVersion, minorDiffVersion);
fprintf(stderr, "Verifying source...");
}
NSString *beforeHash = hashOfTreeWithVersion(source, majorDiffVersion);
if (!beforeHash) {
fprintf(stderr, "\nUnable to calculate hash of tree %s\n", [source fileSystemRepresentation]);
return 1;
}
if (![beforeHash isEqualToString:expectedBeforeHash]) {
fprintf(stderr, "\nSource doesn't have expected hash (%s != %s). Giving up.\n", [expectedBeforeHash UTF8String], [beforeHash UTF8String]);
return 1;
}
if (verbose) {
fprintf(stderr, "\nCopying files...");
}
if (!removeTree(destination)) {
fprintf(stderr, "\nFailed to remove %s\n", [destination fileSystemRepresentation]);
return 1;
}
if (!copyTree(source, destination)) {
fprintf(stderr, "\nFailed to copy %s to %s\n", [source fileSystemRepresentation], [destination fileSystemRepresentation]);
return 1;
}
BOOL hasExtractKeyAvailable = MAJOR_VERSION_IS_AT_LEAST(majorDiffVersion, SUBeigeMajorVersion);
if (verbose) {
fprintf(stderr, "\nPatching...");
}
NSFileManager *fileManager = [[NSFileManager alloc] init];
xar_file_t file;
xar_iter_t iter = xar_iter_new();
for (file = xar_file_first(x, iter); file; file = xar_file_next(iter)) {
NSString *path = @(xar_get_path(file));
NSString *sourceFilePath = [source stringByAppendingPathComponent:path];
NSString *destinationFilePath = [destination stringByAppendingPathComponent:path];
// Don't use -[NSFileManager fileExistsAtPath:] because it will follow symbolic links
BOOL fileExisted = verbose && [fileManager attributesOfItemAtPath:destinationFilePath error:nil];
BOOL removedFile = NO;
const char *value;
if (!xar_prop_get(file, DELETE_KEY, &value) ||
(!hasExtractKeyAvailable && !xar_prop_get(file, DELETE_THEN_EXTRACT_OLD_KEY, &value))) {
if (!removeTree(destinationFilePath)) {
fprintf(stderr, "\n%s or %s: failed to remove %s\n", DELETE_KEY, DELETE_THEN_EXTRACT_OLD_KEY, [destination fileSystemRepresentation]);
return 1;
}
if (!hasExtractKeyAvailable && !xar_prop_get(file, DELETE_KEY, &value)) {
if (verbose) {
fprintf(stderr, "\n%s %s", VERBOSE_DELETED, [path fileSystemRepresentation]);
}
continue;
}
removedFile = YES;
}
if (!xar_prop_get(file, BINARY_DELTA_KEY, &value)) {
if (!applyBinaryDeltaToFile(x, file, sourceFilePath, destinationFilePath)) {
fprintf(stderr, "\nUnable to patch %s to destination %s\n", [sourceFilePath fileSystemRepresentation], [destinationFilePath fileSystemRepresentation]);
return 1;
}
if (verbose) {
fprintf(stderr, "\n🔨 %s %s", VERBOSE_PATCHED, [path fileSystemRepresentation]);
}
} else if ((hasExtractKeyAvailable && !xar_prop_get(file, EXTRACT_KEY, &value)) ||
(!hasExtractKeyAvailable && xar_prop_get(file, MODIFY_PERMISSIONS_KEY, &value))) { // extract and permission modifications don't coexist
if (xar_extract_tofile(x, file, [destinationFilePath fileSystemRepresentation]) != 0) {
fprintf(stderr, "\nUnable to extract file to %s\n", [destinationFilePath fileSystemRepresentation]);
return 1;
}
if (verbose) {
if (fileExisted) {
fprintf(stderr, "\n✏️ %s %s", VERBOSE_UPDATED, [path fileSystemRepresentation]);
} else {
fprintf(stderr, "\n%s %s", VERBOSE_ADDED, [path fileSystemRepresentation]);
}
}
} else if (verbose && removedFile) {
fprintf(stderr, "\n%s %s", VERBOSE_DELETED, [path fileSystemRepresentation]);
}
if (!xar_prop_get(file, MODIFY_PERMISSIONS_KEY, &value)) {
mode_t mode = (mode_t)[[NSString stringWithUTF8String:value] intValue];
if (!modifyPermissions(destinationFilePath, mode)) {
fprintf(stderr, "\nUnable to modify permissions (%s) on file %s\n", value, [destinationFilePath fileSystemRepresentation]);
return 1;
}
if (verbose) {
fprintf(stderr, "\n👮 %s %s (0%o)", VERBOSE_MODIFIED, [path fileSystemRepresentation], mode);
}
}
}
xar_close(x);
if (verbose) {
fprintf(stderr, "\nVerifying destination...");
}
NSString *afterHash = hashOfTreeWithVersion(destination, majorDiffVersion);
if (!afterHash) {
fprintf(stderr, "\nUnable to calculate hash of tree %s\n", [destination fileSystemRepresentation]);
return 1;
}
if (![afterHash isEqualToString:expectedAfterHash]) {
fprintf(stderr, "\nDestination doesn't have expected hash (%s != %s). Giving up.\n", [expectedAfterHash UTF8String], [afterHash UTF8String]);
removeTree(destination);
return 1;
}
if (verbose) {
fprintf(stderr, "\nDone!\n");
}
return 0;
}

View File

@ -1,81 +0,0 @@
//
// SUBinaryDeltaCommon.h
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#ifndef SUBINARYDELTACOMMON_H
#define SUBINARYDELTACOMMON_H
#include <fts.h>
#define PERMISSION_FLAGS (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX)
#define IS_VALID_PERMISSIONS(mode) \
(((mode & PERMISSION_FLAGS) == 0755) || ((mode & PERMISSION_FLAGS) == 0644))
#define BINARY_DELTA_ATTRIBUTES_KEY "binary-delta-attributes"
#define MAJOR_DIFF_VERSION_KEY "major-version"
#define MINOR_DIFF_VERSION_KEY "minor-version"
#define BEFORE_TREE_SHA1_KEY "before-tree-sha1"
#define AFTER_TREE_SHA1_KEY "after-tree-sha1"
#define DELETE_KEY "delete"
#define EXTRACT_KEY "extract"
#define BINARY_DELTA_KEY "binary-delta"
#define MODIFY_PERMISSIONS_KEY "mod-permissions"
// Properties no longer used in new patches
#define DELETE_THEN_EXTRACT_OLD_KEY "delete-then-extract"
#define BEFORE_TREE_SHA1_OLD_KEY "before-sha1"
#define AFTER_TREE_SHA1_OLD_KEY "after-sha1"
#define VERBOSE_DELETED "Deleted" // file is deleted from the file system when applying a patch
#define VERBOSE_REMOVED "Removed" // file is set to be removed when creating a patch
#define VERBOSE_ADDED "Added" // file is added to the patch or file system
#define VERBOSE_DIFFED "Diffed" // file is diffed when creating a patch
#define VERBOSE_PATCHED "Patched" // file is patched when applying a patch
#define VERBOSE_UPDATED "Updated" // file's contents are updated
#define VERBOSE_MODIFIED "Modified" // file's metadata is modified
#define MAJOR_VERSION_IS_AT_LEAST(actualMajor, expectedMajor) (actualMajor >= expectedMajor)
// Each major version will be assigned a name of a color
// Changes that break backwards compatibility will have different major versions
// Changes that affect creating but not applying patches will have different minor versions
typedef NS_ENUM(uint16_t, SUBinaryDeltaMajorVersion)
{
SUAzureMajorVersion = 1,
SUBeigeMajorVersion = 2
};
// Only keep track of the latest minor version for each major version
typedef NS_ENUM(uint16_t, SUBinaryDeltaMinorVersion)
{
SUAzureMinorVersion = 1,
SUBeigeMinorVersion = 0,
};
#define FIRST_DELTA_DIFF_MAJOR_VERSION SUAzureMajorVersion
#define FIRST_DELTA_DIFF_MINOR_VERSION 0
#define LATEST_DELTA_DIFF_MAJOR_VERSION SUBeigeMajorVersion
@class NSString;
@class NSData;
extern int compareFiles(const FTSENT **a, const FTSENT **b);
extern NSData *hashOfFileContents(FTSENT *ent);
extern NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion);
extern NSString *hashOfTree(NSString *path);
extern BOOL removeTree(NSString *path);
extern BOOL copyTree(NSString *source, NSString *dest);
extern BOOL modifyPermissions(NSString *path, mode_t desiredPermissions);
extern NSString *pathRelativeToDirectory(NSString *directory, NSString *path);
NSString *temporaryFilename(NSString *base);
NSString *temporaryDirectory(NSString *base);
NSString *stringWithFileSystemRepresentation(const char*);
SUBinaryDeltaMinorVersion latestMinorVersionForMajorVersion(SUBinaryDeltaMajorVersion majorVersion);
#endif

View File

@ -1,238 +0,0 @@
//
// SUBinaryDeltaCommon.m
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#include "SUBinaryDeltaCommon.h"
#include <CommonCrypto/CommonDigest.h>
#include <Foundation/Foundation.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <xar/xar.h>
int compareFiles(const FTSENT **a, const FTSENT **b)
{
return strcoll((*a)->fts_name, (*b)->fts_name);
}
NSString *pathRelativeToDirectory(NSString *directory, NSString *path)
{
NSUInteger directoryLength = [directory length];
if ([path hasPrefix:directory])
return [path substringFromIndex:directoryLength];
return path;
}
NSString *stringWithFileSystemRepresentation(const char *input) {
return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:input length:strlen(input)];
}
SUBinaryDeltaMinorVersion latestMinorVersionForMajorVersion(SUBinaryDeltaMajorVersion majorVersion)
{
switch (majorVersion) {
case SUAzureMajorVersion:
return SUAzureMinorVersion;
case SUBeigeMajorVersion:
return SUBeigeMinorVersion;
}
return 0;
}
NSString *temporaryFilename(NSString *base)
{
NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]];
NSMutableData *data = [NSMutableData data];
[data appendBytes:template.fileSystemRepresentation length:strlen(template.fileSystemRepresentation) + 1];
char *buffer = data.mutableBytes;
int fd = mkstemp(buffer);
if (fd == -1) {
perror("mkstemp");
return nil;
}
if (close(fd) != 0) {
perror("close");
return nil;
}
return stringWithFileSystemRepresentation(buffer);
}
NSString *temporaryDirectory(NSString *base)
{
NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]];
NSMutableData *data = [NSMutableData data];
[data appendBytes:template.fileSystemRepresentation length:strlen(template.fileSystemRepresentation) + 1];
char *buffer = data.mutableBytes;
char *templateResult = mkdtemp(buffer);
if (templateResult == NULL) {
perror("mkdtemp");
return nil;
}
return stringWithFileSystemRepresentation(templateResult);
}
static void _hashOfBuffer(unsigned char *hash, const char* buffer, ssize_t bufferLength)
{
assert(bufferLength >= 0 && bufferLength <= UINT32_MAX);
CC_SHA1_CTX hashContext;
CC_SHA1_Init(&hashContext);
CC_SHA1_Update(&hashContext, buffer, (CC_LONG)bufferLength);
CC_SHA1_Final(hash, &hashContext);
}
static BOOL _hashOfFileContents(unsigned char* hash, FTSENT *ent)
{
if (ent->fts_info == FTS_SL) {
char linkDestination[MAXPATHLEN + 1];
ssize_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN);
if (linkDestinationLength < 0) {
perror("readlink");
return NO;
}
_hashOfBuffer(hash, linkDestination, linkDestinationLength);
} else if (ent->fts_info == FTS_F) {
int fileDescriptor = open(ent->fts_path, O_RDONLY);
if (fileDescriptor == -1) {
perror("open");
return NO;
}
ssize_t fileSize = ent->fts_statp->st_size;
if (fileSize == 0) {
_hashOfBuffer(hash, NULL, 0);
} else {
void *buffer = mmap(0, (size_t)fileSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fileDescriptor, 0);
if (buffer == (void*)-1) {
close(fileDescriptor);
perror("mmap");
return NO;
}
_hashOfBuffer(hash, buffer, fileSize);
munmap(buffer, (size_t)fileSize);
}
close(fileDescriptor);
} else if (ent->fts_info == FTS_D) {
memset(hash, 0xdd, CC_SHA1_DIGEST_LENGTH);
} else {
return NO;
}
return YES;
}
NSData *hashOfFileContents(FTSENT *ent)
{
unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
if (!_hashOfFileContents(fileHash, ent)) {
return nil;
}
return [NSData dataWithBytes:fileHash length:CC_SHA1_DIGEST_LENGTH];
}
NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion)
{
const char *sourcePaths[] = {[path fileSystemRepresentation], 0};
FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
if (!fts) {
perror("fts_open");
return nil;
}
CC_SHA1_CTX hashContext;
CC_SHA1_Init(&hashContext);
FTSENT *ent = 0;
while ((ent = fts_read(fts))) {
if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
continue;
if (ent->fts_info == FTS_D && !MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) {
continue;
}
NSString *relativePath = pathRelativeToDirectory(path, stringWithFileSystemRepresentation(ent->fts_path));
if (relativePath.length == 0)
continue;
unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
if (!_hashOfFileContents(fileHash, ent)) {
return nil;
}
CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash));
const char *relativePathBytes = [relativePath fileSystemRepresentation];
CC_SHA1_Update(&hashContext, relativePathBytes, (CC_LONG)strlen(relativePathBytes));
if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) {
uint16_t mode = ent->fts_statp->st_mode;
uint16_t type = ent->fts_info;
uint16_t permissions = mode & PERMISSION_FLAGS;
CC_SHA1_Update(&hashContext, &type, sizeof(type));
CC_SHA1_Update(&hashContext, &permissions, sizeof(permissions));
}
}
fts_close(fts);
unsigned char hash[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(hash, &hashContext);
char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1];
size_t i;
for (i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
sprintf(hexHash + i * 2, "%02x", hash[i]);
return @(hexHash);
}
extern NSString *hashOfTree(NSString *path)
{
return hashOfTreeWithVersion(path, LATEST_DELTA_DIFF_MAJOR_VERSION);
}
BOOL removeTree(NSString *path)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
// Don't use fileExistsForPath: because it will try to follow symbolic links
if (![fileManager attributesOfItemAtPath:path error:nil]) {
return YES;
}
return [fileManager removeItemAtPath:path error:nil];
}
BOOL copyTree(NSString *source, NSString *dest)
{
return [[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:nil];
}
BOOL modifyPermissions(NSString *path, mode_t desiredPermissions)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:nil];
if (!attributes) {
return NO;
}
NSNumber *permissions = attributes[NSFilePosixPermissions];
if (!permissions) {
return NO;
}
mode_t newMode = ([permissions unsignedShortValue] & ~PERMISSION_FLAGS) | desiredPermissions;
int (*changeModeFunc)(const char *, mode_t) = [attributes[NSFileType] isEqualToString:NSFileTypeSymbolicLink] ? lchmod : chmod;
if (changeModeFunc([path fileSystemRepresentation], newMode) != 0) {
return NO;
}
return YES;
}

View File

@ -1,17 +0,0 @@
//
// SUBinaryDeltaCreate.m
// Sparkle
//
// Created by Mayur Pawashe on 4/9/15.
// Copyright (c) 2015 Sparkle Project. All rights reserved.
//
#ifndef SUBINARYDELTACREATE_H
#define SUBINARYDELTACREATE_H
#import "SUBinaryDeltaCommon.h"
@class NSString;
int createBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, SUBinaryDeltaMajorVersion majorVersion, BOOL verbose);
#endif

View File

@ -1,462 +0,0 @@
//
// SUBinaryDeltaCreate.m
// Sparkle
//
// Created by Mayur Pawashe on 4/9/15.
// Copyright (c) 2015 Sparkle Project. All rights reserved.
//
#import "SUBinaryDeltaCreate.h"
#import <Foundation/Foundation.h>
#include "SUBinaryDeltaCommon.h"
#import <CommonCrypto/CommonDigest.h>
#include <fcntl.h>
#include <fts.h>
#include <libgen.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <xar/xar.h>
extern int bsdiff(int argc, const char **argv);
@interface CreateBinaryDeltaOperation : NSOperation
@property (copy) NSString *relativePath;
@property (strong) NSString *resultPath;
@property (strong) NSNumber *oldPermissions;
@property (strong) NSNumber *permissions;
@property (strong) NSString *_fromPath;
@property (strong) NSString *_toPath;
- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree oldPermissions:(NSNumber *)oldPermissions newPermissions:(NSNumber *)permissions;
@end
@implementation CreateBinaryDeltaOperation
@synthesize relativePath = _relativePath;
@synthesize resultPath = _resultPath;
@synthesize oldPermissions = _oldPermissions;
@synthesize permissions = _permissions;
@synthesize _fromPath = _fromPath;
@synthesize _toPath = _toPath;
- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree oldPermissions:(NSNumber *)oldPermissions newPermissions:(NSNumber *)permissions
{
if ((self = [super init])) {
self.relativePath = relativePath;
self.oldPermissions = oldPermissions;
self.permissions = permissions;
self._fromPath = [oldTree stringByAppendingPathComponent:relativePath];
self._toPath = [newTree stringByAppendingPathComponent:relativePath];
}
return self;
}
- (void)main
{
NSString *temporaryFile = temporaryFilename(@"BinaryDelta");
const char *argv[] = {"/usr/bin/bsdiff", [self._fromPath fileSystemRepresentation], [self._toPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]};
int result = bsdiff(4, argv);
if (!result)
self.resultPath = temporaryFile;
}
@end
#define INFO_HASH_KEY @"hash"
#define INFO_TYPE_KEY @"type"
#define INFO_PERMISSIONS_KEY @"permissions"
#define INFO_SIZE_KEY @"size"
static NSDictionary *infoForFile(FTSENT *ent)
{
NSData *hash = hashOfFileContents(ent);
if (!hash) {
return nil;
}
off_t size = (ent->fts_info != FTS_D) ? ent->fts_statp->st_size : 0;
assert(ent->fts_statp != NULL);
mode_t permissions = ent->fts_statp->st_mode & PERMISSION_FLAGS;
return @{INFO_HASH_KEY: hash, INFO_TYPE_KEY: @(ent->fts_info), INFO_PERMISSIONS_KEY : @(permissions), INFO_SIZE_KEY: @(size)};
}
static bool aclExists(const FTSENT *ent)
{
// OS X does not currently support ACLs for symlinks
if (ent->fts_info == FTS_SL) {
return NO;
}
acl_t acl = acl_get_link_np(ent->fts_path, ACL_TYPE_EXTENDED);
if (acl != NULL) {
acl_entry_t entry;
int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
assert(acl_free((void *)acl) == 0);
return (result == 0);
}
return false;
}
static NSString *absolutePath(NSString *path)
{
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
return [[url absoluteURL] path];
}
static NSString *temporaryPatchFile(NSString *patchFile)
{
NSString *path = absolutePath(patchFile);
NSString *directory = [path stringByDeletingLastPathComponent];
NSString *file = [path lastPathComponent];
return [NSString stringWithFormat:@"%@/.%@.tmp", directory, file];
}
#define MIN_FILE_SIZE_FOR_CREATING_DELTA 4096
static BOOL shouldSkipDeltaCompression(NSDictionary* originalInfo, NSDictionary *newInfo)
{
unsigned long long fileSize = [newInfo[INFO_SIZE_KEY] unsignedLongLongValue];
if (fileSize < MIN_FILE_SIZE_FOR_CREATING_DELTA) {
return YES;
}
if (!originalInfo) {
return YES;
}
if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) {
return YES;
}
if ([originalInfo[INFO_HASH_KEY] isEqual:newInfo[INFO_HASH_KEY]]) {
// this is possible if just the permissions have changed
return YES;
}
return NO;
}
static BOOL shouldDeleteThenExtract(NSDictionary* originalInfo, NSDictionary *newInfo)
{
if (!originalInfo) {
return NO;
}
if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) {
return YES;
}
return NO;
}
static BOOL shouldSkipExtracting(NSDictionary *originalInfo, NSDictionary *newInfo)
{
if (!originalInfo) {
return NO;
}
if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) {
return NO;
}
if (![originalInfo[INFO_HASH_KEY] isEqual:newInfo[INFO_HASH_KEY]]) {
return NO;
}
return YES;
}
static BOOL shouldChangePermissions(NSDictionary *originalInfo, NSDictionary *newInfo)
{
if (!originalInfo) {
return NO;
}
if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) {
return NO;
}
if ([originalInfo[INFO_PERMISSIONS_KEY] unsignedShortValue] == [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]) {
return NO;
}
return YES;
}
int createBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, SUBinaryDeltaMajorVersion majorVersion, BOOL verbose)
{
if (majorVersion < FIRST_DELTA_DIFF_MAJOR_VERSION) {
fprintf(stderr, "Version provided (%u) is not valid\n", majorVersion);
return 1;
}
if (majorVersion > LATEST_DELTA_DIFF_MAJOR_VERSION) {
fprintf(stderr, "This program is too old to create a version %u patch, or the version number provided is invalid\n", majorVersion);
return 1;
}
SUBinaryDeltaMinorVersion minorVersion = latestMinorVersionForMajorVersion(majorVersion);
NSMutableDictionary *originalTreeState = [NSMutableDictionary dictionary];
const char *sourcePaths[] = {[source fileSystemRepresentation], 0};
FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
if (!fts) {
perror("fts_open");
return 1;
}
if (verbose) {
fprintf(stderr, "Creating version %u.%u patch...\n", majorVersion, minorVersion);
fprintf(stderr, "Processing %s...", [source fileSystemRepresentation]);
}
FTSENT *ent = 0;
while ((ent = fts_read(fts))) {
if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D) {
continue;
}
NSString *key = pathRelativeToDirectory(source, stringWithFileSystemRepresentation(ent->fts_path));
if (![key length]) {
continue;
}
NSDictionary *info = infoForFile(ent);
if (!info) {
fprintf(stderr, "\nFailed to retrieve info for file %s\n", ent->fts_path);
return 1;
}
originalTreeState[key] = info;
if (aclExists(ent)) {
fprintf(stderr, "\nDiffing ACLs are not supported. Detected ACL in before-tree on file %s\n", ent->fts_path);
return 1;
}
}
fts_close(fts);
NSString *beforeHash = hashOfTreeWithVersion(source, majorVersion);
if (!beforeHash) {
fprintf(stderr, "\nFailed to generate hash for tree %s\n", [source fileSystemRepresentation]);
return 1;
}
NSMutableDictionary *newTreeState = [NSMutableDictionary dictionary];
for (NSString *key in originalTreeState)
{
newTreeState[key] = [NSNull null];
}
if (verbose) {
fprintf(stderr, "\nProcessing %s...", [destination fileSystemRepresentation]);
}
sourcePaths[0] = [destination fileSystemRepresentation];
fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
if (!fts) {
perror("fts_open");
return 1;
}
while ((ent = fts_read(fts))) {
if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D) {
continue;
}
NSString *key = pathRelativeToDirectory(destination, stringWithFileSystemRepresentation(ent->fts_path));
if (![key length]) {
continue;
}
NSDictionary *info = infoForFile(ent);
if (!info) {
fprintf(stderr, "\nFailed to retrieve info from file %s\n", ent->fts_path);
return 1;
}
// We should validate permissions and ACLs even if we don't store the info in the diff in the case of ACLs,
// or in the case of permissions if the patch version is 1
mode_t permissions = [info[INFO_PERMISSIONS_KEY] unsignedShortValue];
if (!IS_VALID_PERMISSIONS(permissions)) {
fprintf(stderr, "\nInvalid file permissions after-tree on file %s\nOnly permissions with modes 0755 and 0644 are supported\n", ent->fts_path);
return 1;
}
if (aclExists(ent)) {
fprintf(stderr, "\nDiffing ACLs are not supported. Detected ACL in after-tree on file %s\n", ent->fts_path);
return 1;
}
NSDictionary *oldInfo = originalTreeState[key];
if ([info isEqual:oldInfo]) {
[newTreeState removeObjectForKey:key];
} else {
newTreeState[key] = info;
if (oldInfo && [oldInfo[INFO_TYPE_KEY] unsignedShortValue] == FTS_D && [info[INFO_TYPE_KEY] unsignedShortValue] != FTS_D) {
NSArray *parentPathComponents = key.pathComponents;
for (NSString *childPath in originalTreeState) {
NSArray *childPathComponents = childPath.pathComponents;
if (childPathComponents.count > parentPathComponents.count &&
[parentPathComponents isEqualToArray:[childPathComponents subarrayWithRange:NSMakeRange(0, parentPathComponents.count)]]) {
[newTreeState removeObjectForKey:childPath];
}
}
}
}
}
fts_close(fts);
NSString *afterHash = hashOfTreeWithVersion(destination, majorVersion);
if (!afterHash) {
fprintf(stderr, "\nFailed to generate hash for tree %s\n", [destination fileSystemRepresentation]);
return 1;
}
if (verbose) {
fprintf(stderr, "\nGenerating delta...");
}
NSString *temporaryFile = temporaryPatchFile(patchFile);
xar_t x = xar_open([temporaryFile fileSystemRepresentation], WRITE);
xar_opt_set(x, XAR_OPT_COMPRESSION, "bzip2");
xar_subdoc_t attributes = xar_subdoc_new(x, BINARY_DELTA_ATTRIBUTES_KEY);
xar_subdoc_prop_set(attributes, MAJOR_DIFF_VERSION_KEY, [[NSString stringWithFormat:@"%u", majorVersion] UTF8String]);
xar_subdoc_prop_set(attributes, MINOR_DIFF_VERSION_KEY, [[NSString stringWithFormat:@"%u", minorVersion] UTF8String]);
// Version 1 patches don't have a major or minor version field, so we need to differentiate between the hash keys
const char *beforeHashKey =
MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) ? BEFORE_TREE_SHA1_KEY : BEFORE_TREE_SHA1_OLD_KEY;
const char *afterHashKey =
MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) ? AFTER_TREE_SHA1_KEY : AFTER_TREE_SHA1_OLD_KEY;
xar_subdoc_prop_set(attributes, beforeHashKey, [beforeHash UTF8String]);
xar_subdoc_prop_set(attributes, afterHashKey, [afterHash UTF8String]);
NSOperationQueue *deltaQueue = [[NSOperationQueue alloc] init];
NSMutableArray *deltaOperations = [NSMutableArray array];
// Sort the keys by preferring the ones from the original tree to appear first
// We want to enforce deleting before extracting in the case paths differ only by case
NSArray *keys = [[newTreeState allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *key1, NSString *key2) {
NSComparisonResult insensitiveCompareResult = [key1 caseInsensitiveCompare:key2];
if (insensitiveCompareResult != NSOrderedSame) {
return insensitiveCompareResult;
}
return originalTreeState[key1] ? NSOrderedAscending : NSOrderedDescending;
}];
for (NSString* key in keys) {
id value = [newTreeState valueForKey:key];
if ([value isEqual:[NSNull null]]) {
xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1);
assert(newFile);
xar_prop_set(newFile, DELETE_KEY, "true");
if (verbose) {
fprintf(stderr, "\n%s %s", VERBOSE_REMOVED, [key fileSystemRepresentation]);
}
continue;
}
NSDictionary *originalInfo = originalTreeState[key];
NSDictionary *newInfo = newTreeState[key];
if (shouldSkipDeltaCompression(originalInfo, newInfo)) {
if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) && shouldSkipExtracting(originalInfo, newInfo)) {
if (shouldChangePermissions(originalInfo, newInfo)) {
xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1);
assert(newFile);
xar_prop_set(newFile, MODIFY_PERMISSIONS_KEY, [[NSString stringWithFormat:@"%u", [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]] UTF8String]);
if (verbose) {
fprintf(stderr, "\n👮 %s %s (0%o -> 0%o)", VERBOSE_MODIFIED, [key fileSystemRepresentation], [originalInfo[INFO_PERMISSIONS_KEY] unsignedShortValue], [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]);
}
}
} else {
NSString *path = [destination stringByAppendingPathComponent:key];
xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]);
assert(newFile);
if (shouldDeleteThenExtract(originalInfo, newInfo)) {
if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) {
xar_prop_set(newFile, DELETE_KEY, "true");
} else {
xar_prop_set(newFile, DELETE_THEN_EXTRACT_OLD_KEY, "true");
}
}
if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) {
xar_prop_set(newFile, EXTRACT_KEY, "true");
}
if (verbose) {
if (originalInfo) {
fprintf(stderr, "\n✏️ %s %s", VERBOSE_UPDATED, [key fileSystemRepresentation]);
} else {
fprintf(stderr, "\n%s %s", VERBOSE_ADDED, [key fileSystemRepresentation]);
}
}
}
} else {
NSNumber *permissions =
(MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) && shouldChangePermissions(originalInfo, newInfo)) ?
newInfo[INFO_PERMISSIONS_KEY] :
nil;
CreateBinaryDeltaOperation *operation = [[CreateBinaryDeltaOperation alloc] initWithRelativePath:key oldTree:source newTree:destination oldPermissions:originalInfo[INFO_PERMISSIONS_KEY] newPermissions:permissions];
[deltaQueue addOperation:operation];
[deltaOperations addObject:operation];
}
}
[deltaQueue waitUntilAllOperationsAreFinished];
for (CreateBinaryDeltaOperation *operation in deltaOperations) {
NSString *resultPath = [operation resultPath];
if (!resultPath) {
fprintf(stderr, "\nFailed to create patch from source %s and destination %s\n", [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]);
return 1;
}
if (verbose) {
fprintf(stderr, "\n🔨 %s %s", VERBOSE_DIFFED, [[operation relativePath] fileSystemRepresentation]);
}
xar_file_t newFile = xar_add_frompath(x, 0, [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]);
assert(newFile);
xar_prop_set(newFile, BINARY_DELTA_KEY, "true");
unlink([resultPath fileSystemRepresentation]);
if (operation.permissions) {
xar_prop_set(newFile, MODIFY_PERMISSIONS_KEY, [[NSString stringWithFormat:@"%u", [operation.permissions unsignedShortValue]] UTF8String]);
if (verbose) {
fprintf(stderr, "\n👮 %s %s (0%o -> 0%o)", VERBOSE_MODIFIED, [[operation relativePath] fileSystemRepresentation], operation.oldPermissions.unsignedShortValue, operation.permissions.unsignedShortValue);
}
}
}
xar_close(x);
unlink([patchFile fileSystemRepresentation]);
link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]);
unlink([temporaryFile fileSystemRepresentation]);
if (verbose) {
fprintf(stderr, "\nDone!\n");
}
return 0;
}

View File

@ -1,218 +0,0 @@
//
// SUBinaryDeltaTool.m
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#include "SUBinaryDeltaApply.h"
#include "SUBinaryDeltaCreate.h"
#import "SUBinaryDeltaCommon.h"
#include <Foundation/Foundation.h>
#include <xar/xar.h>
#define VERBOSE_FLAG @"--verbose"
#define VERSION_FLAG @"--version"
#define CREATE_COMMAND @"create"
#define APPLY_COMMAND @"apply"
#define VERSION_COMMAND @"version"
#define VERSION_ALTERNATE_COMMAND @"--version"
static void printUsage(NSString *programName)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "%s create [--verbose] [--version=<version>] <before-tree> <after-tree> <patch-file>\n", [programName UTF8String]);
fprintf(stderr, "%s apply [--verbose] <before-tree> <after-tree> <patch-file>\n", [programName UTF8String]);
fprintf(stderr, "%s version [<patch-file>]\n", [programName UTF8String]);
}
static int runCreateCommand(NSString *programName, NSArray *args)
{
if (args.count < 3 || args.count > 5) {
printUsage(programName);
return 1;
}
NSUInteger numberOflagsFound = 0;
NSUInteger verboseIndex = [args indexOfObject:VERBOSE_FLAG];
NSUInteger versionIndex = NSNotFound;
for (NSUInteger argumentIndex = 0; argumentIndex < args.count; ++argumentIndex) {
if ([args[argumentIndex] hasPrefix:VERSION_FLAG]) {
versionIndex = argumentIndex;
break;
}
}
if (verboseIndex != NSNotFound) {
++numberOflagsFound;
}
if (versionIndex != NSNotFound) {
++numberOflagsFound;
}
if (args.count - numberOflagsFound < 3) {
printUsage(programName);
return 1;
}
BOOL verbose = (verboseIndex != NSNotFound);
NSString *versionField = (versionIndex != NSNotFound) ? args[versionIndex] : nil;
NSArray *versionComponents = nil;
if (versionField) {
versionComponents = [versionField componentsSeparatedByString:@"="];
if (versionComponents.count != 2) {
printUsage(programName);
return 1;
}
}
SUBinaryDeltaMajorVersion patchVersion =
!versionComponents ?
LATEST_DELTA_DIFF_MAJOR_VERSION :
(SUBinaryDeltaMajorVersion)[[versionComponents[1] componentsSeparatedByString:@"."][0] intValue]; // ignore minor version if provided
NSMutableArray *fileArgs = [NSMutableArray array];
for (NSString *argument in args) {
if (![argument hasPrefix:VERSION_FLAG] && ![argument isEqualToString:VERBOSE_FLAG]) {
[fileArgs addObject:argument];
}
}
if (fileArgs.count != 3) {
printUsage(programName);
return 1;
}
BOOL isDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[0] isDirectory:&isDirectory] || !isDirectory) {
printUsage(programName);
fprintf(stderr, "Error: before-tree must be a directory\n");
return 1;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[1] isDirectory:&isDirectory] || !isDirectory) {
printUsage(programName);
fprintf(stderr, "Error: after-tree must be a directory\n");
return 1;
}
return createBinaryDelta(fileArgs[0], fileArgs[1], fileArgs[2], patchVersion, verbose);
}
static int runApplyCommand(NSString *programName, NSArray *args)
{
if (args.count < 3 || args.count > 4) {
printUsage(programName);
return 1;
}
BOOL verbose = [args containsObject:VERBOSE_FLAG];
if (args.count == 4 && !verbose) {
printUsage(programName);
return 1;
}
NSMutableArray *fileArgs = [NSMutableArray array];
for (NSString *argument in args) {
if (![argument isEqualToString:VERBOSE_FLAG]) {
[fileArgs addObject:argument];
}
}
if (fileArgs.count != 3) {
printUsage(programName);
return 1;
}
BOOL isDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[0] isDirectory:&isDirectory] || !isDirectory) {
printUsage(programName);
fprintf(stderr, "Error: before-tree must be a directory\n");
return 1;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[2] isDirectory:&isDirectory] || isDirectory) {
printUsage(programName);
fprintf(stderr, "Error: patch-file must be a file %d\n", isDirectory);
return 1;
}
return applyBinaryDelta(fileArgs[0], fileArgs[1], fileArgs[2], verbose);
}
static int runVersionCommand(NSString *programName, NSArray *args)
{
if (args.count > 1) {
printUsage(programName);
return 1;
}
if (args.count == 0) {
fprintf(stdout, "%u.%u\n", LATEST_DELTA_DIFF_MAJOR_VERSION, latestMinorVersionForMajorVersion(LATEST_DELTA_DIFF_MAJOR_VERSION));
} else {
NSString *patchFile = args[0];
xar_t x = xar_open([patchFile fileSystemRepresentation], READ);
if (!x) {
fprintf(stderr, "Unable to open patch %s\n", [patchFile fileSystemRepresentation]);
return 1;
}
SUBinaryDeltaMajorVersion majorDiffVersion = FIRST_DELTA_DIFF_MAJOR_VERSION;
SUBinaryDeltaMinorVersion minorDiffVersion = FIRST_DELTA_DIFF_MINOR_VERSION;
xar_subdoc_t subdoc;
for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) {
if (!strcmp(xar_subdoc_name(subdoc), BINARY_DELTA_ATTRIBUTES_KEY)) {
const char *value = 0;
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, MAJOR_DIFF_VERSION_KEY, &value);
if (value)
majorDiffVersion = (SUBinaryDeltaMajorVersion)[@(value) intValue];
// available in version 2.0 or later
xar_subdoc_prop_get(subdoc, MINOR_DIFF_VERSION_KEY, &value);
if (value)
minorDiffVersion = (SUBinaryDeltaMinorVersion)[@(value) intValue];
}
}
fprintf(stdout, "%u.%u\n", majorDiffVersion, minorDiffVersion);
}
return 0;
}
int main(int __unused argc, char __unused *argv[])
{
@autoreleasepool {
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSString *programName = [args[0] lastPathComponent];
if (args.count < 2) {
printUsage(programName);
return 1;
}
NSString *command = args[1];
NSArray *commandArguments = [args subarrayWithRange:NSMakeRange(2, args.count - 2)];
int result;
if ([command isEqualToString:CREATE_COMMAND]) {
result = runCreateCommand(programName, commandArguments);
} else if ([command isEqualToString:APPLY_COMMAND]) {
result = runApplyCommand(programName, commandArguments);
} else if ([command isEqualToString:VERSION_COMMAND] || [command isEqualToString:VERSION_ALTERNATE_COMMAND]) {
result = runVersionCommand(programName, commandArguments);
} else {
result = 1;
printUsage(programName);
}
return result;
}
}

View File

@ -1,19 +0,0 @@
//
// SUBinaryDeltaUnarchiver.h
// Sparkle
//
// Created by Mark Rowe on 2009-06-03.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#ifndef SUBINARYDELTAUNARCHIVER_H
#define SUBINARYDELTAUNARCHIVER_H
#import <Cocoa/Cocoa.h>
#import "SUUnarchiver.h"
@interface SUBinaryDeltaUnarchiver : SUUnarchiver
@end
#endif

View File

@ -1,55 +0,0 @@
//
// SUBinaryDeltaUnarchiver.m
// Sparkle
//
// Created by Mark Rowe on 2009-06-03.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#import "SUBinaryDeltaCommon.h"
#import "SUBinaryDeltaUnarchiver.h"
#import "SUBinaryDeltaApply.h"
#import "SUUnarchiver_Private.h"
#import "SUHost.h"
#import "NTSynchronousTask.h"
@implementation SUBinaryDeltaUnarchiver
+ (BOOL)canUnarchivePath:(NSString *)path
{
return [[path pathExtension] isEqualToString:@"delta"];
}
- (void)applyBinaryDelta
{
@autoreleasepool {
NSString *sourcePath = self.updateHostBundlePath;
NSString *targetPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]];
int result = applyBinaryDelta(sourcePath, targetPath, self.archivePath, NO);
if (!result) {
dispatch_async(dispatch_get_main_queue(), ^{
[self notifyDelegateOfSuccess];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self notifyDelegateOfFailure];
});
}
}
}
- (void)start
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self applyBinaryDelta];
});
}
+ (void)load
{
[self registerImplementation:self];
}
@end

View File

@ -1,21 +0,0 @@
//
// SUCodeSigningVerifier.h
// Sparkle
//
// Created by Andy Matuschak on 7/5/12.
//
//
#ifndef SUCODESIGNINGVERIFIER_H
#define SUCODESIGNINGVERIFIER_H
#import <Foundation/Foundation.h>
@interface SUCodeSigningVerifier : NSObject
+ (BOOL)codeSignatureMatchesHostAndIsValidAtPath:(NSString *)applicationPath error:(NSError **)error;
+ (BOOL)codeSignatureIsValidAtPath:(NSString *)applicationPath error:(NSError **)error;
+ (BOOL)hostApplicationIsCodeSigned;
+ (BOOL)applicationAtPathIsCodeSigned:(NSString *)applicationPath;
@end
#endif

View File

@ -1,198 +0,0 @@
//
// SUCodeSigningVerifier.m
// Sparkle
//
// Created by Andy Matuschak on 7/5/12.
//
//
#include <Security/CodeSigning.h>
#include <Security/SecCode.h>
#import "SUCodeSigningVerifier.h"
#import "SULog.h"
@implementation SUCodeSigningVerifier
+ (BOOL)codeSignatureMatchesHostAndIsValidAtPath:(NSString *)applicationPath error:(NSError *__autoreleasing *)error
{
OSStatus result;
SecRequirementRef requirement = NULL;
SecStaticCodeRef staticCode = NULL;
SecCodeRef hostCode = NULL;
NSBundle *newBundle;
CFErrorRef cfError = NULL;
if (error) {
*error = nil;
}
result = SecCodeCopySelf(kSecCSDefaultFlags, &hostCode);
if (result != noErr) {
SULog(@"Failed to copy host code %d", result);
goto finally;
}
result = SecCodeCopyDesignatedRequirement(hostCode, kSecCSDefaultFlags, &requirement);
if (result != noErr) {
SULog(@"Failed to copy designated requirement. Code Signing OSStatus code: %d", result);
goto finally;
}
newBundle = [NSBundle bundleWithPath:applicationPath];
if (!newBundle) {
SULog(@"Failed to load NSBundle for update");
result = -1;
goto finally;
}
result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode);
if (result != noErr) {
SULog(@"Failed to get static code %d", result);
goto finally;
}
// Note that kSecCSCheckNestedCode may not work with pre-Mavericks code signing.
// See https://github.com/sparkle-project/Sparkle/issues/376#issuecomment-48824267 and https://developer.apple.com/library/mac/technotes/tn2206
SecCSFlags flags = kSecCSDefaultFlags | kSecCSCheckAllArchitectures;
result = SecStaticCodeCheckValidityWithErrors(staticCode, flags, requirement, &cfError);
if (cfError) {
NSError *tmpError = CFBridgingRelease(cfError);
if (error) *error = tmpError;
}
if (result != noErr) {
if (result == errSecCSUnsigned) {
SULog(@"The host app is signed, but the new version of the app is not signed using Apple Code Signing. Please ensure that the new app is signed and that archiving did not corrupt the signature.");
}
if (result == errSecCSReqFailed) {
CFStringRef requirementString = nil;
if (SecRequirementCopyString(requirement, kSecCSDefaultFlags, &requirementString) == noErr) {
SULog(@"Code signature of the new version doesn't match the old version: %@. Please ensure that old and new app is signed using exactly the same certificate.", requirementString);
CFRelease(requirementString);
}
[self logSigningInfoForCode:hostCode label:@"host info"];
[self logSigningInfoForCode:staticCode label:@"new info"];
}
}
finally:
if (hostCode) CFRelease(hostCode);
if (staticCode) CFRelease(staticCode);
if (requirement) CFRelease(requirement);
return (result == noErr);
}
+ (BOOL)codeSignatureIsValidAtPath:(NSString *)applicationPath error:(NSError *__autoreleasing *)error
{
OSStatus result;
SecStaticCodeRef staticCode = NULL;
NSBundle *newBundle;
CFErrorRef cfError = NULL;
if (error) {
*error = nil;
}
newBundle = [NSBundle bundleWithPath:applicationPath];
if (!newBundle) {
SULog(@"Failed to load NSBundle");
result = -1;
goto finally;
}
result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode);
if (result != noErr) {
SULog(@"Failed to get static code %d", result);
goto finally;
}
// Note that kSecCSCheckNestedCode may not work with pre-Mavericks code signing.
// See https://github.com/sparkle-project/Sparkle/issues/376#issuecomment-48824267 and https://developer.apple.com/library/mac/technotes/tn2206
SecCSFlags flags = kSecCSDefaultFlags | kSecCSCheckAllArchitectures;
result = SecStaticCodeCheckValidityWithErrors(staticCode, flags, NULL, &cfError);
if (cfError) {
NSError *tmpError = CFBridgingRelease(cfError);
if (error) *error = tmpError;
}
if (result != noErr) {
if (result == errSecCSUnsigned) {
SULog(@"Error: The app is not signed using Apple Code Signing. %@", applicationPath);
}
if (result == errSecCSReqFailed) {
[self logSigningInfoForCode:staticCode label:@"new info"];
}
}
finally:
if (staticCode) CFRelease(staticCode);
return (result == noErr);
}
static id valueOrNSNull(id value) {
return value ? value : [NSNull null];
}
+ (void)logSigningInfoForCode:(SecStaticCodeRef)code label:(NSString*)label {
CFDictionaryRef signingInfo = nil;
const SecCSFlags flags = kSecCSSigningInformation | kSecCSRequirementInformation | kSecCSDynamicInformation | kSecCSContentInformation;
if (SecCodeCopySigningInformation(code, flags, &signingInfo) == noErr) {
NSDictionary *signingDict = CFBridgingRelease(signingInfo);
NSMutableDictionary *relevantInfo = [NSMutableDictionary dictionary];
for (NSString *key in @[@"format", @"identifier", @"requirements", @"teamid", @"signing-time"]) {
relevantInfo[key] = valueOrNSNull(signingDict[key]);
}
NSDictionary *infoPlist = signingDict[@"info-plist"];
relevantInfo[@"version"] = valueOrNSNull(infoPlist[@"CFBundleShortVersionString"]);
relevantInfo[@"build"] = valueOrNSNull(infoPlist[(__bridge NSString *)kCFBundleVersionKey]);
SULog(@"%@: %@", label, relevantInfo);
}
}
+ (BOOL)hostApplicationIsCodeSigned
{
OSStatus result;
SecCodeRef hostCode = NULL;
result = SecCodeCopySelf(kSecCSDefaultFlags, &hostCode);
if (result != 0) return NO;
SecRequirementRef requirement = NULL;
result = SecCodeCopyDesignatedRequirement(hostCode, kSecCSDefaultFlags, &requirement);
if (hostCode) CFRelease(hostCode);
if (requirement) CFRelease(requirement);
return (result == 0);
}
+ (BOOL)applicationAtPathIsCodeSigned:(NSString *)applicationPath
{
OSStatus result;
SecStaticCodeRef staticCode = NULL;
NSBundle *newBundle;
newBundle = [NSBundle bundleWithPath:applicationPath];
if (!newBundle) {
SULog(@"Failed to load NSBundle");
return NO;
}
result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode);
if (result == errSecCSUnsigned) {
return NO;
}
SecRequirementRef requirement = NULL;
result = SecCodeCopyDesignatedRequirement(staticCode, kSecCSDefaultFlags, &requirement);
if (staticCode) {
CFRelease(staticCode);
}
if (requirement) {
CFRelease(requirement);
}
if (result == errSecCSUnsigned) {
return NO;
}
return (result == 0);
}
@end

View File

@ -1,82 +0,0 @@
//
// SUConstants.h
// Sparkle
//
// Created by Andy Matuschak on 3/16/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUCONSTANTS_H
#define SUCONSTANTS_H
// -----------------------------------------------------------------------------
// Misc:
// -----------------------------------------------------------------------------
extern const NSTimeInterval SUMinimumUpdateCheckInterval;
extern const NSTimeInterval SUDefaultUpdateCheckInterval;
extern NSString *const SUBundleIdentifier;
// -----------------------------------------------------------------------------
// Notifications:
// -----------------------------------------------------------------------------
extern NSString *const SUTechnicalErrorInformationKey;
// -----------------------------------------------------------------------------
// PList keys::
// -----------------------------------------------------------------------------
extern NSString *const SUFeedURLKey;
extern NSString *const SUHasLaunchedBeforeKey;
extern NSString *const SUShowReleaseNotesKey;
extern NSString *const SUSkippedVersionKey;
extern NSString *const SUScheduledCheckIntervalKey;
extern NSString *const SULastCheckTimeKey;
extern NSString *const SUExpectsDSASignatureKey;
extern NSString *const SUPublicDSAKeyKey;
extern NSString *const SUPublicDSAKeyFileKey;
extern NSString *const SUAutomaticallyUpdateKey;
extern NSString *const SUAllowsAutomaticUpdatesKey;
extern NSString *const SUEnableAutomaticChecksKey;
extern NSString *const SUEnableAutomaticChecksKeyOld;
extern NSString *const SUEnableSystemProfilingKey;
extern NSString *const SUSendProfileInfoKey;
extern NSString *const SULastProfileSubmitDateKey;
extern NSString *const SUPromptUserOnFirstLaunchKey;
extern NSString *const SUKeepDownloadOnFailedInstallKey;
extern NSString *const SUDefaultsDomainKey;
extern NSString *const SUFixedHTMLDisplaySizeKey __attribute__((deprecated("This key is obsolete and has no effect.")));
extern NSString *const SUAppendVersionNumberKey;
extern NSString *const SUEnableAutomatedDowngradesKey;
extern NSString *const SUNormalizeInstalledApplicationNameKey;
extern NSString *const SURelaunchToolNameKey;
// -----------------------------------------------------------------------------
// Appcast keys::
// -----------------------------------------------------------------------------
extern NSString *const SUAppcastAttributeDeltaFrom;
extern NSString *const SUAppcastAttributeDSASignature;
extern NSString *const SUAppcastAttributeShortVersionString;
extern NSString *const SUAppcastAttributeVersion;
extern NSString *const SUAppcastElementCriticalUpdate;
extern NSString *const SUAppcastElementDeltas;
extern NSString *const SUAppcastElementMinimumSystemVersion;
extern NSString *const SUAppcastElementMaximumSystemVersion;
extern NSString *const SUAppcastElementReleaseNotesLink;
extern NSString *const SUAppcastElementTags;
extern NSString *const SURSSAttributeURL;
extern NSString *const SURSSElementDescription;
extern NSString *const SURSSElementEnclosure;
extern NSString *const SURSSElementLink;
extern NSString *const SURSSElementPubDate;
extern NSString *const SURSSElementTitle;
#endif

View File

@ -1,73 +0,0 @@
//
// SUConstants.m
// Sparkle
//
// Created by Andy Matuschak on 3/16/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUConstants.h"
#ifndef DEBUG
#define DEBUG 0
#endif
// Define some minimum intervals to avoid DoS-like checking attacks
const NSTimeInterval SUMinimumUpdateCheckInterval = DEBUG ? 60 : (60 * 60);
const NSTimeInterval SUDefaultUpdateCheckInterval = DEBUG ? 60 : (60 * 60 * 24);
NSString *const SUBundleIdentifier = @SPARKLE_BUNDLE_IDENTIFIER;
NSString *const SUTechnicalErrorInformationKey = @"SUTechnicalErrorInformation";
NSString *const SUHasLaunchedBeforeKey = @"SUHasLaunchedBefore";
NSString *const SUFeedURLKey = @"SUFeedURL";
NSString *const SUShowReleaseNotesKey = @"SUShowReleaseNotes";
NSString *const SUSkippedVersionKey = @"SUSkippedVersion";
NSString *const SUScheduledCheckIntervalKey = @"SUScheduledCheckInterval";
NSString *const SULastCheckTimeKey = @"SULastCheckTime";
NSString *const SUExpectsDSASignatureKey = @"SUExpectsDSASignature";
NSString *const SUPublicDSAKeyKey = @"SUPublicDSAKey";
NSString *const SUPublicDSAKeyFileKey = @"SUPublicDSAKeyFile";
NSString *const SUAutomaticallyUpdateKey = @"SUAutomaticallyUpdate";
NSString *const SUAllowsAutomaticUpdatesKey = @"SUAllowsAutomaticUpdates";
NSString *const SUEnableSystemProfilingKey = @"SUEnableSystemProfiling";
NSString *const SUEnableAutomaticChecksKey = @"SUEnableAutomaticChecks";
NSString *const SUEnableAutomaticChecksKeyOld = @"SUCheckAtStartup";
NSString *const SUSendProfileInfoKey = @"SUSendProfileInfo";
NSString *const SULastProfileSubmitDateKey = @"SULastProfileSubmissionDate";
NSString *const SUPromptUserOnFirstLaunchKey = @"SUPromptUserOnFirstLaunch";
NSString *const SUFixedHTMLDisplaySizeKey = @"SUFixedHTMLDisplaySize";
NSString *const SUKeepDownloadOnFailedInstallKey = @"SUKeepDownloadOnFailedInstall";
NSString *const SUDefaultsDomainKey = @"SUDefaultsDomain";
NSString *const SUSparkleErrorDomain = @"SUSparkleErrorDomain";
NSString *const SUAppendVersionNumberKey = @"SUAppendVersionNumber";
NSString *const SUEnableAutomatedDowngradesKey = @"SUEnableAutomatedDowngrades";
NSString *const SUNormalizeInstalledApplicationNameKey = @"SUNormalizeInstalledApplicationName";
NSString *const SURelaunchToolNameKey = @"SURelaunchToolName";
NSString *const SUAppcastAttributeDeltaFrom = @"sparkle:deltaFrom";
NSString *const SUAppcastAttributeDSASignature = @"sparkle:dsaSignature";
NSString *const SUAppcastAttributeShortVersionString = @"sparkle:shortVersionString";
NSString *const SUAppcastAttributeVersion = @"sparkle:version";
NSString *const SUAppcastElementCriticalUpdate = @"sparkle:criticalUpdate";
NSString *const SUAppcastElementDeltas = @"sparkle:deltas";
NSString *const SUAppcastElementMinimumSystemVersion = @"sparkle:minimumSystemVersion";
NSString *const SUAppcastElementMaximumSystemVersion = @"sparkle:maximumSystemVersion";
NSString *const SUAppcastElementReleaseNotesLink = @"sparkle:releaseNotesLink";
NSString *const SUAppcastElementTags = @"sparkle:tags";
NSString *const SURSSAttributeURL = @"url";
NSString *const SURSSElementDescription = @"description";
NSString *const SURSSElementEnclosure = @"enclosure";
NSString *const SURSSElementLink = @"link";
NSString *const SURSSElementPubDate = @"pubDate";
NSString *const SURSSElementTitle = @"title";

View File

@ -1,29 +0,0 @@
//
// SUDSAVerifier.h
// Sparkle
//
// Created by Andy Matuschak on 3/16/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
// Includes code by Zach Waldowski on 10/18/13.
// Copyright 2014 Big Nerd Ranch. Licensed under MIT.
//
// Includes code from Plop by Mark Hamlin.
// Copyright 2011 Mark Hamlin. Licensed under BSD.
//
#ifndef SUDSAVERIFIER_H
#define SUDSAVERIFIER_H
@interface SUDSAVerifier : NSObject
+ (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString;
- (instancetype)initWithPublicKeyData:(NSData *)data;
- (BOOL)verifyFileAtPath:(NSString *)path signature:(NSData *)signature;
- (BOOL)verifyStream:(NSInputStream *)stream signature:(NSData *)signature;
@end
#endif

View File

@ -1,159 +0,0 @@
//
// SUDSAVerifier.m
// Sparkle
//
// Created by Andy Matuschak on 3/16/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
// Includes code by Zach Waldowski on 10/18/13.
// Copyright 2014 Big Nerd Ranch. Licensed under MIT.
//
// Includes code from Plop by Mark Hamlin.
// Copyright 2011 Mark Hamlin. Licensed under BSD.
//
#import "SUDSAVerifier.h"
#import "SULog.h"
#include <CommonCrypto/CommonDigest.h>
@implementation SUDSAVerifier {
SecKeyRef _secKey;
}
+ (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString
{
if (!encodedSignature) {
SULog(@"There is no DSA signature to check");
return NO;
}
if (!path) {
return NO;
}
SUDSAVerifier *verifier = [[self alloc] initWithPublicKeyData:[pkeyString dataUsingEncoding:NSUTF8StringEncoding]];
if (!verifier) {
return NO;
}
NSString *strippedSignature = [encodedSignature stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
NSData *signature = [[NSData alloc] initWithBase64Encoding:strippedSignature];
return [verifier verifyFileAtPath:path signature:signature];
}
- (instancetype)initWithPublicKeyData:(NSData *)data
{
self = [super init];
if (!self || !data.length) {
SULog(@"Could not read public DSA key");
return nil;
}
SecExternalFormat format = kSecFormatOpenSSL;
SecExternalItemType itemType = kSecItemTypePublicKey;
CFArrayRef items = NULL;
OSStatus status = SecItemImport((__bridge CFDataRef)data, NULL, &format, &itemType, 0, NULL, NULL, &items);
if (status != errSecSuccess || !items) {
if (items) {
CFRelease(items);
}
SULog(@"Public DSA key could not be imported: %d", status);
return nil;
}
if (format == kSecFormatOpenSSL && itemType == kSecItemTypePublicKey && CFArrayGetCount(items) == 1) {
_secKey = (SecKeyRef)CFRetain(CFArrayGetValueAtIndex(items, 0));
}
CFRelease(items);
return self;
}
- (void)dealloc
{
if (_secKey) {
CFRelease(_secKey);
}
}
- (BOOL)verifyFileAtPath:(NSString *)path signature:(NSData *)signature
{
if (!path.length) {
return NO;
}
NSInputStream *dataInputStream = [NSInputStream inputStreamWithFileAtPath:path];
return [self verifyStream:dataInputStream signature:signature];
}
- (BOOL)verifyStream:(NSInputStream *)stream signature:(NSData *)signature
{
if (!stream || !signature) {
return NO;
}
__block SecGroupTransformRef group = SecTransformCreateGroupTransform();
__block SecTransformRef dataReadTransform = NULL;
__block SecTransformRef dataDigestTransform = NULL;
__block SecTransformRef dataVerifyTransform = NULL;
__block CFErrorRef error = NULL;
BOOL (^cleanup)(void) = ^{
if (group) CFRelease(group);
if (dataReadTransform) CFRelease(dataReadTransform);
if (dataDigestTransform) CFRelease(dataDigestTransform);
if (dataVerifyTransform) CFRelease(dataVerifyTransform);
if (error) CFRelease(error);
return NO;
};
dataReadTransform = SecTransformCreateReadTransformWithReadStream((__bridge CFReadStreamRef)stream);
if (!dataReadTransform) {
SULog(@"File containing update archive could not be read (failed to create SecTransform for input stream)");
return cleanup();
}
dataDigestTransform = SecDigestTransformCreate(kSecDigestSHA1, CC_SHA1_DIGEST_LENGTH, NULL);
if (!dataDigestTransform) {
return cleanup();
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
dataVerifyTransform = SecVerifyTransformCreate(_secKey, (__bridge CFDataRef)signature, &error);
#pragma clang diagnostic pop
if (!dataVerifyTransform || error) {
SULog(@"Could not understand format of the signature: %@; Signature data: %@", error, signature);
return cleanup();
}
SecTransformConnectTransforms(dataReadTransform, kSecTransformOutputAttributeName, dataDigestTransform, kSecTransformInputAttributeName, group, &error);
if (error) {
SULog(@"%@", error);
return cleanup();
}
SecTransformConnectTransforms(dataDigestTransform, kSecTransformOutputAttributeName, dataVerifyTransform, kSecTransformInputAttributeName, group, &error);
if (error) {
SULog(@"%@", error);
return cleanup();
}
NSNumber *result = CFBridgingRelease(SecTransformExecute(group, &error));
if (error) {
SULog(@"DSA signature verification failed: %@", error);
return cleanup();
}
if (!result.boolValue) {
SULog(@"DSA signature does not match. Data of the update file being checked is different than data that has been signed, or the public key and the private key are not from the same set.");
}
cleanup();
return result.boolValue;
}
@end

View File

@ -1,19 +0,0 @@
//
// SUDiskImageUnarchiver.h
// Sparkle
//
// Created by Andy Matuschak on 6/16/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUDISKIMAGEUNARCHIVER_H
#define SUDISKIMAGEUNARCHIVER_H
#import <Cocoa/Cocoa.h>
#import "SUUnarchiver.h"
@interface SUDiskImageUnarchiver : SUUnarchiver
@end
#endif

View File

@ -1,168 +0,0 @@
//
// SUDiskImageUnarchiver.m
// Sparkle
//
// Created by Andy Matuschak on 6/16/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUDiskImageUnarchiver.h"
#import "SUUnarchiver_Private.h"
#import "NTSynchronousTask.h"
#import "SULog.h"
#include <CoreServices/CoreServices.h>
@implementation SUDiskImageUnarchiver
+ (BOOL)canUnarchivePath:(NSString *)path
{
return [[path pathExtension] isEqualToString:@"dmg"];
}
// Called on a non-main thread.
- (void)extractDMG
{
@autoreleasepool {
[self extractDMGWithPassword:nil];
}
}
// Called on a non-main thread.
- (void)extractDMGWithPassword:(NSString *)__unused password
{
@autoreleasepool {
BOOL mountedSuccessfully = NO;
SULog(@"Extracting %@ as a DMG", self.archivePath);
// get a unique mount point path
NSString *mountPoint = nil;
FSRef tmpRef;
NSFileManager *manager;
NSError *error;
NSArray *contents;
// We have to declare these before a goto to prevent an error under ARC.
// No, we cannot have them in the dispatch_async calls, as the goto "jump enters
// lifetime of block which strongly captures a variable"
dispatch_block_t delegateFailure = ^{
[self notifyDelegateOfFailure];
};
dispatch_block_t delegateSuccess = ^{
[self notifyDelegateOfSuccess];
};
do
{
// Using NSUUID would make creating UUIDs be done in Cocoa,
// and thus managed under ARC. Sadly, the class is in 10.8 and later.
CFUUIDRef uuid = CFUUIDCreate(NULL);
if (uuid)
{
NSString *uuidString = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
if (uuidString)
{
mountPoint = [@"/Volumes" stringByAppendingPathComponent:uuidString];
}
CFRelease(uuid);
}
}
while (noErr == FSPathMakeRefWithOptions((UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL));
NSData *promptData = nil;
promptData = [NSData dataWithBytes:"yes\n" length:4];
NSArray *arguments = @[@"attach", self.archivePath, @"-mountpoint", mountPoint, /*@"-noverify",*/ @"-nobrowse", @"-noautoopen"];
NSData *output = nil;
NSInteger taskResult = -1;
@try
{
NTSynchronousTask *task = [[NTSynchronousTask alloc] init];
[task run:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:promptData];
taskResult = [task result];
output = [[task output] copy];
}
@catch (NSException *)
{
goto reportError;
}
if (taskResult != 0)
{
NSString *resultStr = output ? [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] : nil;
SULog(@"hdiutil failed with code: %ld data: <<%@>>", (long)taskResult, resultStr);
goto reportError;
}
mountedSuccessfully = YES;
// Now that we've mounted it, we need to copy out its contents.
manager = [[NSFileManager alloc] init];
contents = [manager contentsOfDirectoryAtPath:mountPoint error:&error];
if (error)
{
SULog(@"Couldn't enumerate contents of archive mounted at %@: %@", mountPoint, error);
goto reportError;
}
for (NSString *item in contents)
{
NSString *fromPath = [mountPoint stringByAppendingPathComponent:item];
NSString *toPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:item];
// We skip any files in the DMG which are not readable.
if (![manager isReadableFileAtPath:fromPath]) {
continue;
}
SULog(@"copyItemAtPath:%@ toPath:%@", fromPath, toPath);
if (![manager copyItemAtPath:fromPath toPath:toPath error:&error])
{
SULog(@"Couldn't copy item: %@ : %@", error, error.userInfo ? error.userInfo : @"");
goto reportError;
}
}
dispatch_async(dispatch_get_main_queue(), delegateSuccess);
goto finally;
reportError:
dispatch_async(dispatch_get_main_queue(), delegateFailure);
finally:
if (mountedSuccessfully)
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:@[@"detach", mountPoint, @"-force"]];
else
SULog(@"Can't mount DMG %@", self.archivePath);
}
}
- (void)start
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self extractDMG];
});
}
+ (void)load
{
[self registerImplementation:self];
}
- (BOOL)isEncrypted:(NSData *)resultData
{
BOOL result = NO;
if(resultData)
{
NSString *data = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
if ((data != nil) && !NSEqualRanges([data rangeOfString:@"passphrase-count"], NSMakeRange(NSNotFound, 0)))
{
result = YES;
}
}
return result;
}
@end

View File

@ -1,44 +0,0 @@
//
// SUErrors.h
// Sparkle
//
// Created by C.W. Betts on 10/13/14.
// Copyright (c) 2014 Sparkle Project. All rights reserved.
//
#ifndef SUERRORS_H
#define SUERRORS_H
#import <Foundation/Foundation.h>
#import "SUExport.h"
/**
* Error domain used by Sparkle
*/
SU_EXPORT extern NSString *const SUSparkleErrorDomain;
typedef NS_ENUM(OSStatus, SUError) {
// Appcast phase errors.
SUAppcastParseError = 1000,
SUNoUpdateError = 1001,
SUAppcastError = 1002,
SURunningFromDiskImageError = 1003,
// Downlaod phase errors.
SUTemporaryDirectoryError = 2000,
// Extraction phase errors.
SUUnarchivingError = 3000,
SUSignatureError = 3001,
// Installation phase errors.
SUFileCopyFailure = 4000,
SUAuthenticationFailure = 4001,
SUMissingUpdateError = 4002,
SUMissingInstallerToolError = 4003,
SURelaunchError = 4004,
SUInstallationError = 4005,
SUDowngradeError = 4006
};
#endif

View File

@ -1,18 +0,0 @@
//
// SUExport.h
// Sparkle
//
// Created by Jake Petroules on 2014-08-23.
// Copyright (c) 2014 Sparkle Project. All rights reserved.
//
#ifndef SUEXPORT_H
#define SUEXPORT_H
#ifdef BUILDING_SPARKLE
#define SU_EXPORT __attribute__((visibility("default")))
#else
#define SU_EXPORT
#endif
#endif

View File

@ -1,35 +0,0 @@
//
// SUGuidedPackageInstaller.h
// Sparkle
//
// Created by Graham Miln on 14/05/2010.
// Copyright 2010 Dragon Systems Software Limited. All rights reserved.
//
/*!
# Sparkle Guided Installations
A guided installation allows Sparkle to download and install a package (pkg) or multi-package (mpkg) without user interaction.
The installer package is installed using Mac OS X's built-in command line installer, `/usr/sbin/installer`. No installation interface is shown to the user.
A guided installation can be started by applications other than the application being replaced. This is particularly useful where helper applications or agents are used.
## To Do
- Replace the use of `AuthorizationExecuteWithPrivilegesAndWait`. This method remains because it is well supported and tested. Ideally a helper tool or XPC would be used.
*/
#ifndef SUGUIDEDPACKAGEINSTALLER_H
#define SUGUIDEDPACKAGEINSTALLER_H
#import "Sparkle.h"
#import "SUInstaller.h"
@interface SUGuidedPackageInstaller : SUInstaller {
}
/*! Perform the guided installation */
+ (void)performInstallationToPath:(NSString *)path fromPath:(NSString *)installerGuide host:(SUHost *)host versionComparator:(id<SUVersionComparison>)comparator completionHandler:(void (^)(NSError *))completionHandler;
@end
#endif

View File

@ -1,139 +0,0 @@
//
// SUGuidedPackageInstaller.m
// Sparkle
//
// Created by Graham Miln on 14/05/2010.
// Copyright 2010 Dragon Systems Software Limited. All rights reserved.
//
#import <sys/stat.h>
#import <Security/Security.h>
#import "SUGuidedPackageInstaller.h"
static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authorization, const char* executablePath, AuthorizationFlags options, const char* const* arguments)
{
sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL);
BOOL returnValue = YES;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
/* AuthorizationExecuteWithPrivileges used to support 10.4+; should be replaced with XPC or external process */
if (AuthorizationExecuteWithPrivileges(authorization, executablePath, options, (char* const*)arguments, NULL) == errAuthorizationSuccess)
#pragma clang diagnostic pop
{
int status = 0;
pid_t pid = wait(&status);
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
returnValue = NO;
}
else
returnValue = NO;
signal(SIGCHLD, oldSigChildHandler);
return returnValue;
}
@implementation SUGuidedPackageInstaller (SUGuidedPackageInstallerAuthentication)
+ (AuthorizationRef)authorizationForExecutable:(NSString*)executablePath
{
NSParameterAssert(executablePath);
// Get authorization using advice in Apple's Technical Q&A1172
// ...create authorization without specific rights
AuthorizationRef auth = NULL;
OSStatus validAuth = AuthorizationCreate(NULL,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&auth);
// ...then extend authorization with desired rights
if ((validAuth == errAuthorizationSuccess) &&
(auth != NULL))
{
const char* executableFileSystemRepresentation = [executablePath fileSystemRepresentation];
// Prepare a right allowing script to execute with privileges
AuthorizationItem right;
memset(&right,0,sizeof(right));
right.name = kAuthorizationRightExecute;
right.value = (void*) executableFileSystemRepresentation;
right.valueLength = strlen(executableFileSystemRepresentation);
// Package up the single right
AuthorizationRights rights;
memset(&rights,0,sizeof(rights));
rights.count = 1;
rights.items = &right;
// Extend rights to run script
validAuth = AuthorizationCopyRights(auth,
&rights,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights |
kAuthorizationFlagInteractionAllowed,
NULL);
if (validAuth != errAuthorizationSuccess)
{
// Error, clean up authorization
(void) AuthorizationFree(auth,kAuthorizationFlagDefaults);
auth = NULL;
}
}
return auth;
}
@end
@implementation SUGuidedPackageInstaller
+ (void)performInstallationToPath:(NSString *)destinationPath fromPath:(NSString *)packagePath host:(SUHost *)__unused host versionComparator:(id<SUVersionComparison>)__unused comparator completionHandler:(void (^)(NSError *))completionHandler
{
NSParameterAssert(packagePath);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Preflight
NSString* installerPath = @"/usr/sbin/installer"; // Mac OS X 10.2+ command line installer tool
NSError* error = nil;
// Create authorization for installer executable
BOOL validInstallation = NO;
AuthorizationRef auth = [self authorizationForExecutable:installerPath];
if (auth != NULL)
{
// Permission was granted to execute the installer with privileges
const char* const arguments[] = {
"-pkg",
[packagePath fileSystemRepresentation],
"-target",
"/",
NULL
};
validInstallation = AuthorizationExecuteWithPrivilegesAndWait(auth,
[installerPath fileSystemRepresentation],
kAuthorizationFlagDefaults,
arguments);
// TODO: wait for communications pipe to close via fileno & CFSocketCreateWithNative
AuthorizationFree(auth,kAuthorizationFlagDefaults);
}
else
{
NSString* errorMessage = [NSString stringWithFormat:@"Sparkle Updater: Script authorization denied."];
error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:[NSDictionary dictionaryWithObject:errorMessage forKey:NSLocalizedDescriptionKey]];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self finishInstallationToPath:destinationPath
withResult:validInstallation
error:error
completionHandler:completionHandler];
});
});
}
@end

View File

@ -1,40 +0,0 @@
//
// SUHost.h
// Sparkle
//
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
@interface SUHost : NSObject
@property (strong, readonly) NSBundle *bundle;
+ (NSString *)systemVersionString;
- (instancetype)initWithBundle:(NSBundle *)aBundle;
@property (readonly, copy) NSString *bundlePath;
@property (readonly, copy) NSString *appCachePath;
@property (readonly, copy) NSString *installationPath;
@property (readonly, copy) NSString *name;
@property (readonly, copy) NSString *version;
@property (readonly, copy) NSString *displayVersion;
@property (readonly, copy) NSImage *icon;
@property (getter=isRunningOnReadOnlyVolume, readonly) BOOL runningOnReadOnlyVolume;
@property (getter=isBackgroundApplication, readonly) BOOL backgroundApplication;
@property (readonly, copy) NSString *publicDSAKey;
@property (readonly, copy) NSArray *systemProfile;
- (id)objectForInfoDictionaryKey:(NSString *)key;
- (BOOL)boolForInfoDictionaryKey:(NSString *)key;
- (id)objectForUserDefaultsKey:(NSString *)defaultName;
- (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName;
- (BOOL)boolForUserDefaultsKey:(NSString *)defaultName;
- (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName;
- (id)objectForKey:(NSString *)key;
- (BOOL)boolForKey:(NSString *)key;
@end

View File

@ -1,287 +0,0 @@
//
// SUHost.m
// Sparkle
//
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUHost.h"
#import "SUConstants.h"
#import "SUSystemProfiler.h"
#include <sys/mount.h> // For statfs for isRunningOnReadOnlyVolume
#import "SULog.h"
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101000
typedef struct {
NSInteger majorVersion;
NSInteger minorVersion;
NSInteger patchVersion;
} NSOperatingSystemVersion;
@interface NSProcessInfo ()
- (NSOperatingSystemVersion)operatingSystemVersion;
@end
#endif
@interface SUHost ()
@property (strong, readwrite) NSBundle *bundle;
@property (copy) NSString *defaultsDomain;
@property (assign) BOOL usesStandardUserDefaults;
@end
@implementation SUHost
@synthesize bundle;
@synthesize defaultsDomain;
@synthesize usesStandardUserDefaults;
- (instancetype)initWithBundle:(NSBundle *)aBundle
{
if ((self = [super init]))
{
if (aBundle == nil) aBundle = [NSBundle mainBundle];
self.bundle = aBundle;
if (![self.bundle bundleIdentifier]) {
SULog(@"Error: the bundle being updated at %@ has no %@! This will cause preference read/write to not work properly.", self.bundle, kCFBundleIdentifierKey);
}
self.defaultsDomain = [self.bundle objectForInfoDictionaryKey:SUDefaultsDomainKey];
if (!self.defaultsDomain) {
self.defaultsDomain = [self.bundle bundleIdentifier];
}
// If we're using the main bundle's defaults we'll use the standard user defaults mechanism, otherwise we have to get CF-y.
usesStandardUserDefaults = !self.defaultsDomain || [self.defaultsDomain isEqualToString:[[NSBundle mainBundle] bundleIdentifier]];
}
return self;
}
- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self bundlePath], [self installationPath]]; }
- (NSString *)bundlePath
{
return [self.bundle bundlePath];
}
- (NSString *)appCachePath
{
NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = nil;
if ([cachePaths count]) {
cachePath = cachePaths[0];
}
if (!cachePath) {
SULog(@"Failed to find user's cache directory! Using system default");
cachePath = NSTemporaryDirectory();
}
NSString *name = [self.bundle bundleIdentifier];
if (!name) {
name = [self name];
}
cachePath = [cachePath stringByAppendingPathComponent:name];
cachePath = [cachePath stringByAppendingPathComponent:@"Sparkle"];
return cachePath;
}
- (NSString *)installationPath
{
if ([[[NSBundle bundleWithIdentifier:SUBundleIdentifier] infoDictionary][SUNormalizeInstalledApplicationNameKey] boolValue]) {
// We'll install to "#{CFBundleName}.app", but only if that path doesn't already exist. If we're "Foo 4.2.app," and there's a "Foo.app" in this directory, we don't want to overwrite it! But if there's no "Foo.app," we'll take that name.
NSString *normalizedAppPath = [[[self.bundle bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self.bundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey], [[self.bundle bundlePath] pathExtension]]];
if (![[NSFileManager defaultManager] fileExistsAtPath:[[[self.bundle bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self.bundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey], [[self.bundle bundlePath] pathExtension]]]]) {
return normalizedAppPath;
}
}
return [self.bundle bundlePath];
}
- (NSString *)name
{
NSString *name;
// Allow host bundle to provide a custom name
name = [self objectForInfoDictionaryKey:@"SUBundleName"];
if (name) return name;
name = [self.bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
if (name) return name;
name = [self objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey];
if (name) return name;
return [[[NSFileManager defaultManager] displayNameAtPath:[self.bundle bundlePath]] stringByDeletingPathExtension];
}
- (NSString *)version
{
NSString *version = [self.bundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey];
if (!version || [version isEqualToString:@""])
[NSException raise:@"SUNoVersionException" format:@"This host (%@) has no %@! This attribute is required.", [self bundlePath], (__bridge NSString *)kCFBundleVersionKey];
return version;
}
- (NSString *)displayVersion
{
NSString *shortVersionString = [self.bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
if (shortVersionString)
return shortVersionString;
else
return [self version]; // Fall back on the normal version string.
}
- (NSImage *)icon
{
// Cache the application icon.
NSString *iconPath = [self.bundle pathForResource:[self.bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:@"icns"];
// According to the OS X docs, "CFBundleIconFile - This key identifies the file containing
// the icon for the bundle. The filename you specify does not need to include the .icns
// extension, although it may."
//
// However, if it *does* include the '.icns' the above method fails (tested on OS X 10.3.9) so we'll also try:
if (!iconPath) {
iconPath = [self.bundle pathForResource:[self.bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:nil];
}
NSImage *icon = [[NSImage alloc] initWithContentsOfFile:iconPath];
// Use a default icon if none is defined.
if (!icon) {
BOOL isMainBundle = (self.bundle == [NSBundle mainBundle]);
NSString *fileType = isMainBundle ? (NSString *)kUTTypeApplication : (NSString *)kUTTypeBundle;
icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType];
}
return icon;
}
- (BOOL)isRunningOnReadOnlyVolume
{
struct statfs statfs_info;
statfs([[self.bundle bundlePath] fileSystemRepresentation], &statfs_info);
return (statfs_info.f_flags & MNT_RDONLY);
}
- (BOOL)isBackgroundApplication
{
return ([[NSApplication sharedApplication] activationPolicy] == NSApplicationActivationPolicyAccessory);
}
- (NSString *)publicDSAKey
{
// Maybe the key is just a string in the Info.plist.
NSString *key = [self.bundle objectForInfoDictionaryKey:SUPublicDSAKeyKey];
if (key) { return key; }
// More likely, we've got a reference to a Resources file by filename:
NSString *keyFilename = [self objectForInfoDictionaryKey:SUPublicDSAKeyFileKey];
if (!keyFilename) { return nil; }
NSError *ignoreErr = nil;
return [NSString stringWithContentsOfFile:[self.bundle pathForResource:keyFilename ofType:nil] encoding:NSASCIIStringEncoding error:&ignoreErr];
}
- (NSArray *)systemProfile
{
return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHost:self];
}
- (id)objectForInfoDictionaryKey:(NSString *)key
{
return [self.bundle objectForInfoDictionaryKey:key];
}
- (BOOL)boolForInfoDictionaryKey:(NSString *)key
{
return [[self objectForInfoDictionaryKey:key] boolValue];
}
- (id)objectForUserDefaultsKey:(NSString *)defaultName
{
if (!defaultName || !self.defaultsDomain) {
return nil;
}
// Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistrationDomain, so anything
// passed into -[NSUserDefaults registerDefaults:] is ignored. The following line falls
// back to using NSUserDefaults, but only if the host bundle is the main bundle.
if (self.usesStandardUserDefaults) {
return [[NSUserDefaults standardUserDefaults] objectForKey:defaultName];
}
CFPropertyListRef obj = CFPreferencesCopyAppValue((__bridge CFStringRef)defaultName, (__bridge CFStringRef)self.defaultsDomain);
return CFBridgingRelease(obj);
}
- (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName
{
if (self.usesStandardUserDefaults)
{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:defaultName];
}
else
{
CFPreferencesSetValue((__bridge CFStringRef)defaultName, (__bridge CFPropertyListRef)(value), (__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
CFPreferencesSynchronize((__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
}
}
- (BOOL)boolForUserDefaultsKey:(NSString *)defaultName
{
if (self.usesStandardUserDefaults) {
return [[NSUserDefaults standardUserDefaults] boolForKey:defaultName];
}
BOOL value;
CFPropertyListRef plr = CFPreferencesCopyAppValue((__bridge CFStringRef)defaultName, (__bridge CFStringRef)self.defaultsDomain);
if (plr == NULL) {
value = NO;
}
else
{
value = (BOOL)CFBooleanGetValue((CFBooleanRef)plr);
CFRelease(plr);
}
return value;
}
- (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName
{
if (self.usesStandardUserDefaults)
{
[[NSUserDefaults standardUserDefaults] setBool:value forKey:defaultName];
}
else
{
CFPreferencesSetValue((__bridge CFStringRef)defaultName, (__bridge CFBooleanRef) @(value), (__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
CFPreferencesSynchronize((__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
}
}
- (id)objectForKey:(NSString *)key {
return [self objectForUserDefaultsKey:key] ? [self objectForUserDefaultsKey:key] : [self objectForInfoDictionaryKey:key];
}
- (BOOL)boolForKey:(NSString *)key {
return [self objectForUserDefaultsKey:key] ? [self boolForUserDefaultsKey:key] : [self boolForInfoDictionaryKey:key];
}
+ (NSString *)systemVersionString
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090 // Present in 10.9 despite NS_AVAILABLE's claims
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wselector"
// Xcode 5.1.1: operatingSystemVersion is clearly declared, must warn due to a compiler bug?
if (![NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)])
#pragma clang diagnostic pop
{
NSURL *coreServices = [[NSFileManager defaultManager] URLForDirectory:NSCoreServiceDirectory inDomain:NSSystemDomainMask appropriateForURL:nil create:NO error:nil];
return [NSDictionary dictionaryWithContentsOfURL:[coreServices URLByAppendingPathComponent:@"SystemVersion.plist"]][@"ProductVersion"];
}
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
return [NSString stringWithFormat:@"%ld.%ld.%ld", (long)version.majorVersion, (long)version.minorVersion, (long)version.patchVersion];
}
@end

View File

@ -1,25 +0,0 @@
//
// SUInstaller.h
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUINSTALLER_H
#define SUINSTALLER_H
#import <Cocoa/Cocoa.h>
#import "SUVersionComparisonProtocol.h"
@class SUHost;
@interface SUInstaller : NSObject
+ (NSString *)appPathInUpdateFolder:(NSString *)updateFolder forHost:(SUHost *)host;
+ (void)installFromUpdateFolder:(NSString *)updateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath versionComparator:(id<SUVersionComparison>)comparator completionHandler:(void (^)(NSError *))completionHandler;
+ (void)finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result error:(NSError *)error completionHandler:(void (^)(NSError *))completionHandler;
+ (NSString *)updateFolder;
@end
#endif

View File

@ -1,187 +0,0 @@
//
// SUInstaller.m
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUInstaller.h"
#import "SUPlainInstaller.h"
#import "SUPackageInstaller.h"
#import "SUGuidedPackageInstaller.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;
OSStatus err = noErr;
Boolean aliasFileFlag = false, folderFlag = false;
NSURL *fileURL = [NSURL fileURLWithPath:path];
if (FALSE == CFURLGetFSRef((CFURLRef)fileURL, &fileRef))
err = coreFoundationUnknownErr;
if (noErr == err)
err = FSIsAliasFile(&fileRef, &aliasFileFlag, &folderFlag);
if (noErr == err)
return !!(aliasFileFlag && folderFlag);
else
return NO;
}
+ (NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolder forHost:(SUHost *)host isPackage:(BOOL *)isPackagePtr isGuided:(BOOL *)isGuidedPtr
{
NSParameterAssert(inUpdateFolder);
NSParameterAssert(host);
// Search subdirectories for the application
NSString *currentFile,
*newAppDownloadPath = nil,
*bundleFileName = [[host bundlePath] lastPathComponent],
*alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]];
BOOL isPackage = NO;
BOOL isGuided = NO;
NSString *fallbackPackagePath = nil;
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:inUpdateFolder];
NSString *bundleFileNameNoExtension = [bundleFileName stringByDeletingPathExtension];
sUpdateFolder = inUpdateFolder;
while ((currentFile = [dirEnum nextObject])) {
NSString *currentPath = [inUpdateFolder stringByAppendingPathComponent:currentFile];
NSString *currentFilename = [currentFile lastPathComponent];
NSString *currentExtension = [currentFile pathExtension];
NSString *currentFilenameNoExtension = [currentFilename stringByDeletingPathExtension];
if ([currentFilename isEqualToString:bundleFileName] ||
[currentFilename isEqualToString:alternateBundleFileName]) // We found one!
{
isPackage = NO;
newAppDownloadPath = currentPath;
break;
} else if ([currentExtension isEqualToString:@"pkg"] ||
[currentExtension isEqualToString:@"mpkg"]) {
if ([currentFilenameNoExtension isEqualToString:bundleFileNameNoExtension]) {
isPackage = YES;
newAppDownloadPath = currentPath;
break;
} else {
// Remember any other non-matching packages we have seen should we need to use one of them as a fallback.
fallbackPackagePath = currentPath;
}
} else {
// Try matching on bundle identifiers in case the user has changed the name of the host app
NSBundle *incomingBundle = [NSBundle bundleWithPath:currentPath];
if (incomingBundle && [[incomingBundle bundleIdentifier] isEqualToString:[[host bundle] bundleIdentifier]]) {
isPackage = NO;
newAppDownloadPath = currentPath;
break;
}
}
// Some DMGs have symlinks into /Applications! That's no good!
if ([self isAliasFolderAtPath:currentPath])
[dirEnum skipDescendents];
}
// We don't have a valid path. Try to use the fallback package.
if (newAppDownloadPath == nil && fallbackPackagePath != nil) {
isPackage = YES;
newAppDownloadPath = fallbackPackagePath;
}
if (isPackage) {
// foo.app -> foo.sparkle_guided.pkg or foo.sparkle_guided.mpkg
if ([[[newAppDownloadPath stringByDeletingPathExtension] pathExtension] isEqualToString:@"sparkle_guided"]) {
isGuided = YES;
}
}
if (isPackagePtr)
*isPackagePtr = isPackage;
if (isGuidedPtr)
*isGuidedPtr = isGuided;
if (!newAppDownloadPath) {
SULog(@"Searched %@ for %@.(app|pkg)", inUpdateFolder, bundleFileNameNoExtension);
}
return newAppDownloadPath;
}
+ (NSString *)appPathInUpdateFolder:(NSString *)updateFolder forHost:(SUHost *)host
{
BOOL isPackage = NO;
NSString *path = [self installSourcePathInUpdateFolder:updateFolder forHost:host isPackage:&isPackage isGuided:nil];
return isPackage ? nil : path;
}
+ (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath versionComparator:(id<SUVersionComparison>)comparator completionHandler:(void (^)(NSError *))completionHandler
{
BOOL isPackage = NO;
BOOL isGuided = NO;
NSString *newAppDownloadPath = [self installSourcePathInUpdateFolder:inUpdateFolder forHost:host isPackage:&isPackage isGuided:&isGuided];
if (newAppDownloadPath == nil) {
[self finishInstallationToPath:installationPath withResult:NO error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't find an appropriate update in the downloaded package." }] completionHandler:completionHandler];
} else {
if (isPackage && isGuided) {
[SUGuidedPackageInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host versionComparator:comparator completionHandler:completionHandler];
} else if (isPackage) {
[SUPackageInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host versionComparator:comparator completionHandler:completionHandler];
} else {
[SUPlainInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host versionComparator:comparator completionHandler:completionHandler];
}
}
}
+ (void)mdimportInstallationPath:(NSString *)installationPath
{
// *** GETS CALLED ON NON-MAIN THREAD!
SULog(@"mdimporting");
NSTask *mdimport = [[NSTask alloc] init];
[mdimport setLaunchPath:@"/usr/bin/mdimport"];
[mdimport setArguments:@[installationPath]];
@try {
[mdimport launch];
[mdimport waitUntilExit];
}
@catch (NSException *launchException)
{
// No big deal.
SULog(@"Error: %@", [launchException description]);
}
}
+ (void)finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result error:(NSError *)error completionHandler:(void (^)(NSError *))completionHandler
{
if (result) {
[self mdimportInstallationPath:installationPath];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil);
});
} else {
if (!error) {
error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:nil];
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(error);
});
}
}
@end

View File

@ -1,32 +0,0 @@
/*
* 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.
*/
#ifndef SULOG_H
#define SULOG_H
// -----------------------------------------------------------------------------
// Headers:
// -----------------------------------------------------------------------------
#include <Foundation/Foundation.h>
// -----------------------------------------------------------------------------
// Prototypes:
// -----------------------------------------------------------------------------
void SUClearLog(void);
void SULog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
#endif

View File

@ -1,78 +0,0 @@
/*
* SULog.m
* EyeTV
*
* Created by Uli Kusterer on 12/03/2009.
* Copyright 2009 Elgato Systems GmbH. All rights reserved.
*
*/
// -----------------------------------------------------------------------------
// Headers:
// -----------------------------------------------------------------------------
#include "SULog.h"
// -----------------------------------------------------------------------------
// Constants:
// -----------------------------------------------------------------------------
static NSString *const SULogFilePath = @"~/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.
// -----------------------------------------------------------------------------
void SUClearLog(void)
{
FILE *logfile = fopen([[SULogFilePath stringByExpandingTildeInPath] fileSystemRepresentation], "w");
if (logfile) {
fclose(logfile);
SULog(@"===== %@ =====", [[NSFileManager defaultManager] displayNameAtPath:[[NSBundle mainBundle] bundlePath]]);
}
}
// -----------------------------------------------------------------------------
// 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, ...)
{
static BOOL loggedYet = NO;
if (!loggedYet) {
loggedYet = YES;
SUClearLog();
}
va_list ap;
va_start(ap, format);
NSString *theStr = [[NSString alloc] initWithFormat:format arguments:ap];
NSLog(@"Sparkle: %@", theStr);
FILE *logfile = fopen([[SULogFilePath stringByExpandingTildeInPath] fileSystemRepresentation], "a");
if (logfile) {
theStr = [NSString stringWithFormat:@"%@: %@\n", [NSDate date], theStr];
NSData *theData = [theStr dataUsingEncoding:NSUTF8StringEncoding];
fwrite([theData bytes], 1, [theData length], logfile);
fclose(logfile);
}
va_end(ap);
}

View File

@ -1,22 +0,0 @@
//
// SUPackageInstaller.h
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUPACKAGEINSTALLER_H
#define SUPACKAGEINSTALLER_H
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUPlainInstaller.h"
@interface SUPackageInstaller : SUPlainInstaller
@end
#endif

View File

@ -1,41 +0,0 @@
//
// SUPackageInstaller.m
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUPackageInstaller.h"
#import <Cocoa/Cocoa.h>
#import "SUConstants.h"
@implementation SUPackageInstaller
+ (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)__unused host versionComparator:(id<SUVersionComparison>)__unused comparator completionHandler:(void (^)(NSError *))completionHandler
{
// Run installer using the "open" command to ensure it is launched in front of current application.
// -W = wait until the app has quit.
// -n = Open another instance if already open.
// -b = app bundle identifier
NSString *command = @"/usr/bin/open";
NSArray *args = @[@"-W", @"-n", @"-b", @"com.apple.installer", path];
if (![[NSFileManager defaultManager] fileExistsAtPath:command]) {
NSError *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingInstallerToolError userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't find Apple's installer tool!" }];
[self finishInstallationToPath:installationPath withResult:NO error:error completionHandler:completionHandler];
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTask *installer = [NSTask launchedTaskWithLaunchPath:command arguments:args];
[installer waitUntilExit];
// Known bug: if the installation fails or is canceled, Sparkle goes ahead and restarts, thinking everything is fine.
dispatch_async(dispatch_get_main_queue(), ^{
[self finishInstallationToPath:installationPath withResult:YES error:nil completionHandler:completionHandler];
});
});
}
@end

View File

@ -1,19 +0,0 @@
//
// SUPipedUnarchiver.h
// Sparkle
//
// Created by Andy Matuschak on 6/16/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUPIPEDUNARCHIVER_H
#define SUPIPEDUNARCHIVER_H
#import <Cocoa/Cocoa.h>
#import "SUUnarchiver.h"
@interface SUPipedUnarchiver : SUUnarchiver
@end
#endif

View File

@ -1,157 +0,0 @@
//
// SUPipedUnarchiver.m
// Sparkle
//
// Created by Andy Matuschak on 6/16/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUPipedUnarchiver.h"
#import "SUUnarchiver_Private.h"
#import "SULog.h"
@implementation SUPipedUnarchiver
+ (SEL)selectorConformingToTypeOfPath:(NSString *)path
{
static NSDictionary *typeSelectorDictionary;
if (!typeSelectorDictionary)
typeSelectorDictionary = @{ @".zip": @"extractZIP",
@".tar": @"extractTAR",
@".tar.gz": @"extractTGZ",
@".tgz": @"extractTGZ",
@".tar.bz2": @"extractTBZ",
@".tbz": @"extractTBZ",
@".tar.xz": @"extractTXZ",
@".txz": @"extractTXZ",
@".tar.lzma": @"extractTXZ"};
NSString *lastPathComponent = [path lastPathComponent];
for (NSString *currentType in typeSelectorDictionary)
{
if ([currentType length] > [lastPathComponent length]) continue;
if ([[lastPathComponent substringFromIndex:[lastPathComponent length] - [currentType length]] isEqualToString:currentType])
return NSSelectorFromString(typeSelectorDictionary[currentType]);
}
return NULL;
}
- (void)start
{
[NSThread detachNewThreadSelector:[[self class] selectorConformingToTypeOfPath:self.archivePath] toTarget:self withObject:nil];
}
+ (BOOL)canUnarchivePath:(NSString *)path
{
return ([self selectorConformingToTypeOfPath:path] != nil);
}
// This method abstracts the types that use a command line tool piping data from stdin.
- (void)extractArchivePipingDataToCommand:(NSString *)command args:(NSArray*)args
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
@autoreleasepool {
NSString *destination = [self.archivePath stringByDeletingLastPathComponent];
SULog(@"Extracting using '%@' '%@' < '%@' '%@'", command, [args componentsJoinedByString:@"' '"], self.archivePath, destination);
// Get the file size.
NSUInteger expectedLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:self.archivePath error:nil][NSFileSize] unsignedIntegerValue];
if (expectedLength > 0) {
NSFileHandle *archiveInput = [NSFileHandle fileHandleForReadingAtPath:self.archivePath];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *archiveOutput = [pipe fileHandleForWriting];
NSTask *task = [[NSTask alloc] init];
[task setStandardInput:[pipe fileHandleForReading]];
[task setStandardError:[NSFileHandle fileHandleWithStandardError]];
[task setStandardOutput:[NSFileHandle fileHandleWithStandardOutput]];
[task setLaunchPath:command];
[task setArguments:[args arrayByAddingObject:destination]];
[task launch];
NSUInteger bytesRead = 0;
do {
NSData *data = [archiveInput readDataOfLength:256*1024];
NSUInteger len = [data length];
if (!len) {
break;
}
bytesRead += len;
[archiveOutput writeData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[self notifyDelegateOfProgress:(double)bytesRead / (double)expectedLength];
});
}
while(bytesRead < expectedLength);
[archiveOutput closeFile];
[task waitUntilExit];
if ([task terminationStatus] == 0) {
if (bytesRead == expectedLength) {
dispatch_async(dispatch_get_main_queue(), ^{
[self notifyDelegateOfSuccess];
});
return;
} else {
SULog(@"Extraction failed, command '%@' got only %ld of %ld bytes", command, (long)bytesRead, (long)expectedLength);
}
} else {
SULog(@"Extraction failed, command '%@' returned %d", command, [task terminationStatus]);
}
} else {
SULog(@"Extraction failed, archive '%@' is empty", self.archivePath);
}
dispatch_async(dispatch_get_main_queue(), ^{
[self notifyDelegateOfFailure];
});
}
}
- (void)extractTAR
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
[self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-xC"]];
}
- (void)extractTGZ
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
[self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-zxC"]];
}
- (void)extractTBZ
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
[self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-jxC"]];
}
- (void)extractZIP
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
[self extractArchivePipingDataToCommand:@"/usr/bin/ditto" args:@[@"-x",@"-k",@"-"]];
}
- (void)extractTXZ
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
[self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-zxC"]];
}
+ (void)load
{
[self registerImplementation:self];
}
@end

View File

@ -1,28 +0,0 @@
//
// SUPlainInstaller.h
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUPLAININSTALLER_H
#define SUPLAININSTALLER_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)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)host versionComparator:(id<SUVersionComparison>)comparator completionHandler:(void (^)(NSError *))completionHandler;
@end
#endif

View File

@ -1,61 +0,0 @@
//
// SUPlainInstaller.m
// Sparkle
//
// Created by Andy Matuschak on 4/10/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUPlainInstaller.h"
#import "SUPlainInstallerInternals.h"
#import "SUCodeSigningVerifier.h"
#import "SUConstants.h"
#import "SUHost.h"
@implementation SUPlainInstaller
+ (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)host versionComparator:(id<SUVersionComparison>)comparator completionHandler:(void (^)(NSError *))completionHandler
{
NSParameterAssert(host);
// Prevent malicious downgrades
if (![[[NSBundle bundleWithIdentifier:SUBundleIdentifier] infoDictionary][SUEnableAutomatedDowngradesKey] boolValue]) {
if ([comparator compareVersion:[host version] toVersion:[[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]] == NSOrderedDescending) {
NSString *errorMessage = [NSString stringWithFormat:@"Sparkle Updater: Possible attack in progress! Attempting to \"upgrade\" from %@ to %@. Aborting update.", [host version], [[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]];
NSError *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUDowngradeError userInfo:@{ NSLocalizedDescriptionKey: errorMessage }];
[self finishInstallationToPath:installationPath withResult:NO error:error completionHandler:completionHandler];
return;
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSString *oldPath = [host bundlePath];
NSString *tempName = [self temporaryNameForPath:[host installationPath]];
BOOL hostIsCodeSigned = [SUCodeSigningVerifier applicationAtPathIsCodeSigned:oldPath];
BOOL result = [self copyPathWithAuthentication:path overPath:installationPath temporaryName:tempName error:&error];
if (result) {
// If the host is code signed, then the replacement should be be too (and the signature should be valid).
BOOL needToCheckCodeSignature = (hostIsCodeSigned || [SUCodeSigningVerifier applicationAtPathIsCodeSigned:installationPath]);
if (needToCheckCodeSignature) {
result = [SUCodeSigningVerifier codeSignatureIsValidAtPath:installationPath error:&error];
}
}
if (result) {
BOOL haveOld = [[NSFileManager defaultManager] fileExistsAtPath:oldPath];
BOOL differentFromNew = ![oldPath isEqualToString:installationPath];
if (haveOld && differentFromNew) {
[self _movePathToTrash:oldPath]; // On success, trash old copy if there's still one due to renaming.
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self finishInstallationToPath:installationPath withResult:result error:error completionHandler:completionHandler];
});
});
}
@end

View File

@ -1,22 +0,0 @@
//
// SUPlainInstallerInternals.m
// Sparkle
//
// Created by Andy Matuschak on 3/9/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUPLAININSTALLERINTERNALS_H
#define SUPLAININSTALLERINTERNALS_H
#import "SUPlainInstaller.h"
@interface SUPlainInstaller (Internals)
+ (NSString *)temporaryNameForPath:(NSString *)path;
+ (BOOL)copyPathWithAuthentication:(NSString *)src overPath:(NSString *)dst temporaryName:(NSString *)tmp error:(NSError **)error;
+ (void)_movePathToTrash:(NSString *)path;
+ (BOOL)_removeFileAtPath:(NSString *)path error:(NSError **)error;
+ (BOOL)_removeFileAtPathWithForcedAuthentication:(NSString *)src error:(NSError **)error;
@end
#endif

View File

@ -1,628 +0,0 @@
//
// SUPlainInstallerInternals.m
// Sparkle
//
// Created by Andy Matuschak on 3/9/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUPlainInstallerInternals.h"
#import "SUConstants.h"
#import "SULog.h"
#include <CoreServices/CoreServices.h>
#include <Security/Security.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/param.h>
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101000
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
#endif
static inline void PerformOnMainThreadSync(dispatch_block_t theBlock)
{
if ([NSThread isMainThread]) {
theBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), theBlock);
}
}
@interface SUPlainInstaller (MMExtendedAttributes)
// Removes the directory tree rooted at |root| from the file quarantine.
// The quarantine was introduced on OS X 10.5 and is described at:
//
// http://developer.apple.com/releasenotes/Carbon/RN-LaunchServices/index.html
//#apple_ref/doc/uid/TP40001369-DontLinkElementID_2
//
// If |root| is not a directory, then it alone is removed from the quarantine.
// Symbolic links, including |root| if it is a symbolic link, will not be
// traversed.
//
// Ordinarily, the quarantine is managed by calling LSSetItemAttribute
// to set the kLSItemQuarantineProperties attribute to a dictionary specifying
// the quarantine properties to be applied. However, it does not appear to be
// possible to remove an item from the quarantine directly through any public
// Launch Services calls. Instead, this method takes advantage of the fact
// that the quarantine is implemented in part by setting an extended attribute,
// "com.apple.quarantine", on affected files. Removing this attribute is
// sufficient to remove files from the quarantine.
+ (void)releaseFromQuarantine:(NSString *)root;
@end
// Authorization code based on generous contribution from Allan Odgaard. Thanks, Allan!
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" // this is terrible; will fix later probably
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;
if (AuthorizationExecuteWithPrivileges(authorization, executablePath, options, (char* const*)arguments, NULL) == errAuthorizationSuccess)
{
int status;
pid_t pid = wait(&status);
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
returnValue = NO;
}
else
returnValue = NO;
signal(SIGCHLD, oldSigChildHandler);
return returnValue;
}
#pragma clang diagnostic pop
@implementation SUPlainInstaller (Internals)
+ (NSString *)temporaryNameForPath:(NSString *)path
{
// Let's try to read the version number so the filename will be more meaningful.
NSString *postFix;
NSString *version;
if ((version = [[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]) && ![version isEqualToString:@""])
{
NSMutableCharacterSet *validCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[validCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-()"]];
postFix = [version stringByTrimmingCharactersInSet:[validCharacters invertedSet]];
}
else
postFix = @"old";
NSString *prefix = [[path stringByDeletingPathExtension] stringByAppendingFormat:@" (%@)", postFix];
NSString *tempDir = [prefix stringByAppendingPathExtension:[path pathExtension]];
// Now let's make sure we get a unique path.
unsigned int cnt = 2;
while ([[NSFileManager defaultManager] fileExistsAtPath:tempDir] && cnt <= 999)
tempDir = [NSString stringWithFormat:@"%@ %u.%@", prefix, cnt++, [path pathExtension]];
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;
memset(&catInfo, 0, sizeof(catInfo));
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 = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:(char *)trashPath length:strlen((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 *prefix;
if ([[[NSBundle bundleWithIdentifier:SUBundleIdentifier] infoDictionary][SUAppendVersionNumberKey] boolValue]) {
NSString *postFix = nil;
NSString *version = nil;
if ((version = [[NSBundle bundleWithPath: path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]) && ![version isEqualToString:@""])
{
NSMutableCharacterSet *validCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[validCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-()"]];
postFix = [version stringByTrimmingCharactersInSet:[validCharacters invertedSet]];
}
else {
postFix = @"old";
}
prefix = [NSString stringWithFormat:@"%@ (%@)", [[path lastPathComponent] stringByDeletingPathExtension], postFix];
} else {
prefix = [[path lastPathComponent] stringByDeletingPathExtension];
}
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 *__autoreleasing *)error
{
// *** MUST BE SAFE TO CALL ON NON-MAIN THREAD!
const char *srcPath = [src fileSystemRepresentation];
const char *tmpPath = [tmp fileSystemRepresentation];
const char *dstPath = [dst fileSystemRepresentation];
struct stat dstSB;
if (stat(dstPath, &dstSB) != 0) // Doesn't exist yet, try containing folder.
{
const char *dstDirPath = [[dst stringByDeletingLastPathComponent] fileSystemRepresentation];
if( stat(dstDirPath, &dstSB) != 0 )
{
NSString *errorMessage = [NSString stringWithFormat:@"Stat on %@ during authenticated file copy failed.", dst];
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage }];
return NO;
}
}
AuthorizationRef auth = NULL;
OSStatus authStat = errAuthorizationDenied;
while (authStat == errAuthorizationDenied) {
authStat = AuthorizationCreate(NULL,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&auth);
}
BOOL res = NO;
if (authStat == errAuthorizationSuccess) {
res = YES;
char uidgid[42];
snprintf(uidgid, sizeof(uidgid), "%u:%u",
dstSB.st_uid, dstSB.st_gid);
// If the currently-running application is trusted, the new
// version should be trusted as well. Remove it from the
// quarantine to avoid a delay at launch, and to avoid
// presenting the user with a confusing trust dialog.
//
// This needs to be done after the application is moved to its
// new home with "mv" in case it's moved across filesystems: if
// that happens, "mv" actually performs a copy and may result
// in the application being quarantined. It also needs to be
// done before "chown" changes ownership, because the ownership
// change will almost certainly make it impossible to change
// attributes to release the files from the quarantine.
if (res)
{
SULog(@"releaseFromQuarantine");
PerformOnMainThreadSync(^{
[self releaseFromQuarantine:src];
});
}
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);
if (!res)
SULog(@"chown -R %s %s failed.", uidgid, srcPath);
}
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);
if (!res)
SULog(@"rm failed");
}
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)
SULog(@"mv 1 failed");
}
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)
SULog(@"mv 2 failed");
}
// 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 );
// }
AuthorizationFree(auth, 0);
// If the currently-running application is trusted, the new
// version should be trusted as well. Remove it from the
// quarantine to avoid a delay at launch, and to avoid
// presenting the user with a confusing trust dialog.
//
// This needs to be done after the application is moved to its
// new home with "mv" in case it's moved across filesystems: if
// that happens, "mv" actually performs a copy and may result
// in the application being quarantined.
if (res)
{
SULog(@"releaseFromQuarantine after installing");
PerformOnMainThreadSync(^{
[self releaseFromQuarantine:dst];
});
}
if (!res)
{
// Something went wrong somewhere along the way, but we're not sure exactly where.
NSString *errorMessage = [NSString stringWithFormat:@"Authenticated file copy from %@ to %@ failed.", src, dst];
if (error != nil)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage }];
}
}
else
{
if (error != nil)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't get permission to authenticate." }];
}
return res;
}
+ (BOOL)_movePathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst error:(NSError *__autoreleasing *)error
{
// *** MUST BE SAFE TO CALL ON NON-MAIN THREAD!
const char *srcPath = [src fileSystemRepresentation];
const char *dstPath = [dst fileSystemRepresentation];
const char *dstContainerPath = [[dst stringByDeletingLastPathComponent] fileSystemRepresentation];
struct stat dstSB;
stat(dstContainerPath, &dstSB);
AuthorizationRef auth = NULL;
OSStatus authStat = errAuthorizationDenied;
while( authStat == errAuthorizationDenied )
{
authStat = AuthorizationCreate(NULL,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&auth);
}
BOOL res = NO;
if (authStat == errAuthorizationSuccess)
{
res = YES;
char uidgid[42];
snprintf(uidgid, sizeof(uidgid), "%d:%d",
dstSB.st_uid, dstSB.st_gid);
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);
if (!res)
SULog(@"Can't set permissions");
}
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", dstPath, NULL };
res = AuthorizationExecuteWithPrivilegesAndWait(auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams);
if (!res)
SULog(@"Can't remove destination file");
}
if (res) // Move!.
{
const char *mvParams[] = { "-f", srcPath, dstPath, NULL };
res = AuthorizationExecuteWithPrivilegesAndWait(auth, "/bin/mv", kAuthorizationFlagDefaults, mvParams);
if (!res)
SULog(@"Can't move source file");
}
AuthorizationFree(auth, 0);
if (!res)
{
// Something went wrong somewhere along the way, but we're not sure exactly where.
NSString *errorMessage = [NSString stringWithFormat:@"Authenticated file move from %@ to %@ failed.", src, dst];
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage }];
}
}
else
{
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't get permission to authenticate." }];
}
return res;
}
+ (BOOL)_removeFileAtPathWithForcedAuthentication:(NSString *)src error:(NSError *__autoreleasing *)error
{
// *** MUST BE SAFE TO CALL ON NON-MAIN THREAD!
const char *srcPath = [src fileSystemRepresentation];
AuthorizationRef auth = NULL;
OSStatus authStat = errAuthorizationDenied;
while( authStat == errAuthorizationDenied )
{
authStat = AuthorizationCreate(NULL,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&auth);
}
BOOL res = NO;
if (authStat == errAuthorizationSuccess)
{
res = YES;
if (res) // If there's something at our tmp path (previous failed update or whatever) delete that first.
{
const char *rmParams[] = { "-rf", srcPath, NULL };
res = AuthorizationExecuteWithPrivilegesAndWait(auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams);
if (!res)
SULog(@"Can't remove destination file");
}
AuthorizationFree(auth, 0);
if (!res)
{
// Something went wrong somewhere along the way, but we're not sure exactly where.
NSString *errorMessage = [NSString stringWithFormat:@"Authenticated file remove from %@ failed.", src];
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage }];
}
}
else
{
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't get permission to authenticate." }];
}
return res;
}
+ (BOOL)_removeFileAtPath:(NSString *)path error:(NSError *__autoreleasing *)error
{
BOOL success = YES;
if( ![[NSFileManager defaultManager] removeItemAtPath: path error: NULL] )
{
success = [self _removeFileAtPathWithForcedAuthentication:path error:error];
}
return success;
}
+ (void)_movePathToTrash:(NSString *)path
{
//SULog(@"Moving %@ to the trash.", path);
NSInteger tag = 0;
if (![[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[path stringByDeletingLastPathComponent] destination:@"" files:@[[path lastPathComponent]] tag:&tag])
{
BOOL didFindTrash = NO;
NSString *trashPath = [self _temporaryCopyNameForPath:path didFindTrash:&didFindTrash];
if( didFindTrash )
{
NSError *err = nil;
if (![self _movePathWithForcedAuthentication:path toPath:trashPath error:&err]) {
SULog(@"Error: couldn't move %@ to the trash (%@). %@", path, trashPath, err);
}
}
else {
SULog(@"Error: couldn't move %@ to the trash. This is often a sign of a permissions error.", path);
}
}
}
+ (BOOL)copyPathWithAuthentication:(NSString *)src overPath:(NSString *)dst temporaryName:(NSString *)__unused tmp error:(NSError *__autoreleasing *)error
{
FSRef srcRef, dstRef, dstDirRef, tmpDirRef;
OSStatus err;
BOOL hadFileAtDest = NO, didFindTrash = NO;
NSString *tmpPath = [self _temporaryCopyNameForPath:dst didFindTrash:&didFindTrash];
// Make FSRef for destination:
err = FSPathMakeRefWithOptions((UInt8 *)[dst fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &dstRef, NULL);
hadFileAtDest = (err == noErr); // There is a file at the destination, move it aside. If we normalized the name, we might not get here, so don't error.
if( hadFileAtDest )
{
if (0 != access([dst fileSystemRepresentation], W_OK) || 0 != access([[dst stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK))
{
return [self _copyPathWithForcedAuthentication:src toPath:dst temporaryPath:tmpPath error:error];
}
}
else
{
if (0 != access([[dst stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK)
|| 0 != access([[[dst stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK))
{
return [self _copyPathWithForcedAuthentication:src toPath:dst temporaryPath:tmpPath error:error];
}
}
if( hadFileAtDest )
{
err = FSPathMakeRef((UInt8 *)[[tmpPath stringByDeletingLastPathComponent] fileSystemRepresentation], &tmpDirRef, NULL);
if (err != noErr)
FSPathMakeRef((UInt8 *)[[dst stringByDeletingLastPathComponent] fileSystemRepresentation], &tmpDirRef, NULL);
}
err = FSPathMakeRef((UInt8 *)[[dst stringByDeletingLastPathComponent] fileSystemRepresentation], &dstDirRef, NULL);
if (err == noErr && hadFileAtDest)
{
NSFileManager *manager = [[NSFileManager alloc] init];
BOOL success = [manager moveItemAtPath:dst toPath:tmpPath error:error];
if (!success && hadFileAtDest)
{
if (error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Couldn't move %@ to %@.", dst, tmpPath] }];
return NO;
}
}
err = FSPathMakeRef((UInt8 *)[src fileSystemRepresentation], &srcRef, NULL);
if (err == noErr)
{
NSFileManager *manager = [[NSFileManager alloc] init];
BOOL success = [manager copyItemAtPath:src toPath:dst error:error];
if (!success)
{
// We better move the old version back to its old location
if (hadFileAtDest) {
success = [manager moveItemAtPath:tmpPath toPath:dst error:error];
}
if (!success && error != NULL)
*error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUFileCopyFailure userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Couldn't move %@ to %@.", dst, tmpPath] }];
return NO;
}
}
// If the currently-running application is trusted, the new
// version should be trusted as well. Remove it from the
// quarantine to avoid a delay at launch, and to avoid
// presenting the user with a confusing trust dialog.
//
// This needs to be done after the application is moved to its
// new home in case it's moved across filesystems: if that
// happens, the move is actually a copy, and it may result
// in the application being quarantined.
PerformOnMainThreadSync(^{
[self releaseFromQuarantine:dst];
});
return YES;
}
@end
#include <dlfcn.h>
#include <errno.h>
#include <sys/xattr.h>
@implementation SUPlainInstaller (MMExtendedAttributes)
+ (int)removeXAttr:(NSString *)name
fromFile:(NSString *)file
options:(int)options
{
// *** MUST BE SAFE TO CALL ON NON-MAIN THREAD!
const char *path = NULL;
const char *attr = [name cStringUsingEncoding:NSASCIIStringEncoding];
@try {
path = [file fileSystemRepresentation];
}
@catch (id) {
// -[NSString fileSystemRepresentation] throws an exception if it's
// unable to convert the string to something suitable. Map that to
// EDOM, "argument out of domain", which sort of conveys that there
// was a conversion failure.
errno = EDOM;
return -1;
}
return removexattr(path, attr, options);
}
+ (void)releaseFromQuarantine:(NSString *)root
{
// *** MUST BE SAFE TO CALL ON NON-MAIN THREAD!
NSFileManager *manager = [NSFileManager defaultManager];
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101000
if (!&NSURLQuarantinePropertiesKey) {
NSString *const quarantineAttribute = (NSString*)kLSItemQuarantineProperties;
const int removeXAttrOptions = XATTR_NOFOLLOW;
[self removeXAttr:quarantineAttribute
fromFile:root
options:removeXAttrOptions];
// Only recurse if it's actually a directory. Don't recurse into a
// root-level symbolic link.
NSDictionary *rootAttributes = [manager attributesOfItemAtPath:root error:nil];
NSString *rootType = rootAttributes[NSFileType];
if (rootType == NSFileTypeDirectory) {
// The NSDirectoryEnumerator will avoid recursing into any contained
// symbolic links, so no further type checks are needed.
NSDirectoryEnumerator *directoryEnumerator = [manager enumeratorAtPath:root];
NSString *file = nil;
while ((file = [directoryEnumerator nextObject])) {
[self removeXAttr:quarantineAttribute
fromFile:[root stringByAppendingPathComponent:file]
options:removeXAttrOptions];
}
}
return;
}
#endif
NSURL *rootURL = [NSURL fileURLWithPath:root];
id rootResourceValue = nil;
[rootURL getResourceValue:&rootResourceValue forKey:NSURLQuarantinePropertiesKey error:NULL];
if (rootResourceValue) {
[rootURL setResourceValue:[NSNull null] forKey:NSURLQuarantinePropertiesKey error:NULL];
}
// Only recurse if it's actually a directory. Don't recurse into a
// root-level symbolic link.
NSDictionary *rootAttributes = [manager attributesOfItemAtPath:root error:nil];
NSString *rootType = rootAttributes[NSFileType];
if (rootType == NSFileTypeDirectory) {
// The NSDirectoryEnumerator will avoid recursing into any contained
// symbolic links, so no further type checks are needed.
NSDirectoryEnumerator *directoryEnumerator = [manager enumeratorAtURL:rootURL includingPropertiesForKeys:nil options:(NSDirectoryEnumerationOptions)0 errorHandler:nil];
for (NSURL *file in directoryEnumerator) {
id fileResourceValue = nil;
[file getResourceValue:&fileResourceValue forKey:NSURLQuarantinePropertiesKey error:NULL];
if (fileResourceValue) {
[file setResourceValue:[NSNull null] forKey:NSURLQuarantinePropertiesKey error:NULL];
}
}
}
}
@end

View File

@ -1,19 +0,0 @@
//
// SUProbingUpdateDriver.h
// Sparkle
//
// Created by Andy Matuschak on 5/7/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUPROBINGUPDATEDRIVER_H
#define SUPROBINGUPDATEDRIVER_H
#import <Cocoa/Cocoa.h>
#import "SUBasicUpdateDriver.h"
// This replaces the old SUStatusChecker.
@interface SUProbingUpdateDriver : SUBasicUpdateDriver
@end
#endif

View File

@ -1,39 +0,0 @@
//
// SUProbingUpdateDriver.m
// Sparkle
//
// Created by Andy Matuschak on 5/7/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUProbingUpdateDriver.h"
#import "SUUpdater.h"
@implementation SUProbingUpdateDriver
// Stop as soon as we have an answer! Since the superclass implementations are not called, we are responsible for notifying the delegate.
- (void)didFindValidUpdate
{
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:didFindValidUpdate:)])
[updaterDelegate updater:self.updater didFindValidUpdate:self.updateItem];
NSDictionary *userInfo = (self.updateItem != nil) ? @{ SUUpdaterAppcastItemNotificationKey: self.updateItem } : nil;
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFindValidUpdateNotification object:self.updater userInfo:userInfo];
[self abortUpdate];
}
- (void)didNotFindUpdate
{
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updaterDidNotFindUpdate:)]) {
[updaterDelegate updaterDidNotFindUpdate:self.updater];
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater];
[self abortUpdate];
}
@end

View File

@ -1,19 +0,0 @@
//
// SUScheduledUpdateDriver.h
// Sparkle
//
// Created by Andy Matuschak on 5/6/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#ifndef SUSCHEDULEDUPDATEDRIVER_H
#define SUSCHEDULEDUPDATEDRIVER_H
#import <Cocoa/Cocoa.h>
#import "SUUIBasedUpdateDriver.h"
@interface SUScheduledUpdateDriver : SUUIBasedUpdateDriver
@end
#endif

View File

@ -1,60 +0,0 @@
//
// SUScheduledUpdateDriver.m
// Sparkle
//
// Created by Andy Matuschak on 5/6/08.
// Copyright 2008 Andy Matuschak. All rights reserved.
//
#import "SUScheduledUpdateDriver.h"
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
@interface SUScheduledUpdateDriver ()
@property (assign) BOOL showErrors;
@end
@implementation SUScheduledUpdateDriver
@synthesize showErrors;
- (void)didFindValidUpdate
{
self.showErrors = YES; // We only start showing errors after we present the UI for the first time.
[super didFindValidUpdate];
}
- (void)didNotFindUpdate
{
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updaterDidNotFindUpdate:)]) {
[updaterDelegate updaterDidNotFindUpdate:self.updater];
}
[[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater];
[self abortUpdate]; // Don't tell the user that no update was found; this was a scheduled update.
}
- (void)abortUpdateWithError:(NSError *)error
{
if (self.showErrors) {
[super abortUpdateWithError:error];
} else {
// Call delegate separately here because otherwise it won't know we stopped.
// Normally this gets called by the superclass
id<SUUpdaterDelegate> updaterDelegate = [self.updater delegate];
if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) {
[updaterDelegate updater:self.updater didAbortWithError:error];
}
[self abortUpdate];
}
}
@end

View File

@ -1,37 +0,0 @@
//
// SUStandardVersionComparator.h
// Sparkle
//
// Created by Andy Matuschak on 12/21/07.
// Copyright 2007 Andy Matuschak. All rights reserved.
//
#ifndef SUSTANDARDVERSIONCOMPARATOR_H
#define SUSTANDARDVERSIONCOMPARATOR_H
#import "SUExport.h"
#import "SUVersionComparisonProtocol.h"
/*!
Sparkle's default version comparator.
This comparator is adapted from MacPAD, by Kevin Ballard.
It's "dumb" in that it does essentially string comparison,
in components split by character type.
*/
SU_EXPORT @interface SUStandardVersionComparator : NSObject <SUVersionComparison>
/*!
Returns a singleton instance of the comparator.
*/
+ (SUStandardVersionComparator *)defaultComparator;
/*!
Compares version strings through textual analysis.
See the implementation for more details.
*/
- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB;
@end
#endif

View File

@ -1,170 +0,0 @@
//
// SUStandardVersionComparator.m
// Sparkle
//
// Created by Andy Matuschak on 12/21/07.
// Copyright 2007 Andy Matuschak. All rights reserved.
//
#import "SUUpdater.h"
#import "SUAppcast.h"
#import "SUAppcastItem.h"
#import "SUVersionComparisonProtocol.h"
#import "SUStandardVersionComparator.h"
@implementation SUStandardVersionComparator
+ (SUStandardVersionComparator *)defaultComparator
{
static SUStandardVersionComparator *defaultComparator = nil;
if (defaultComparator == nil) {
defaultComparator = [[SUStandardVersionComparator alloc] init];
}
return defaultComparator;
}
typedef NS_ENUM(NSInteger, SUCharacterType) {
kNumberType,
kStringType,
kSeparatorType,
};
- (SUCharacterType)typeOfCharacter:(NSString *)character
{
if ([character isEqualToString:@"."]) {
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;
}
}
- (NSArray *)splitVersionString:(NSString *)version
{
NSString *character;
NSMutableString *s;
NSUInteger i, n;
SUCharacterType oldType, newType;
NSMutableArray *parts = [NSMutableArray array];
if ([version length] == 0) {
// Nothing to do here
return parts;
}
s = [[version substringToIndex:1] mutableCopy];
oldType = [self typeOfCharacter:s];
n = [version length] - 1;
for (i = 1; i <= n; ++i) {
character = [version substringWithRange:NSMakeRange(i, 1)];
newType = [self typeOfCharacter:character];
if (oldType != newType || oldType == kSeparatorType) {
// We've reached a new segment
NSString *aPart = [[NSString alloc] initWithString:s];
[parts addObject:aPart];
[s setString:character];
} else {
// Add character to string and continue
[s appendString:character];
}
oldType = newType;
}
// Add the last part onto the array
[parts addObject:[NSString stringWithString:s]];
return parts;
}
- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB
{
NSArray *partsA = [self splitVersionString:versionA];
NSArray *partsB = [self splitVersionString:versionB];
NSString *partA, *partB;
NSUInteger i, n;
long long valueA, valueB;
SUCharacterType typeA, typeB;
n = MIN([partsA count], [partsB count]);
for (i = 0; i < n; ++i) {
partA = partsA[i];
partB = partsB[i];
typeA = [self typeOfCharacter:partA];
typeB = [self typeOfCharacter:partB];
// Compare types
if (typeA == typeB) {
// Same type; we can compare
if (typeA == kNumberType) {
valueA = [partA longLongValue];
valueB = [partB longLongValue];
if (valueA > valueB) {
return NSOrderedDescending;
} else if (valueA < valueB) {
return NSOrderedAscending;
}
} else if (typeA == kStringType) {
NSComparisonResult result = [partA compare:partB];
if (result != NSOrderedSame) {
return result;
}
}
} else {
// Not the same type? Now we have to do some validity checking
if (typeA != kStringType && typeB == kStringType) {
// typeA wins
return NSOrderedDescending;
} else if (typeA == kStringType && typeB != kStringType) {
// typeB wins
return NSOrderedAscending;
} else {
// One is a number and the other is a period. The period is invalid
if (typeA == kNumberType) {
return NSOrderedDescending;
} else {
return NSOrderedAscending;
}
}
}
}
// The versions are equal up to the point where they both still have parts
// Lets check to see if one is larger than the other
if ([partsA count] != [partsB count]) {
// Yep. Lets get the next part of the larger
// n holds the index of the part we want.
NSString *missingPart;
SUCharacterType missingType;
NSComparisonResult shorterResult, largerResult;
if ([partsA count] > [partsB count]) {
missingPart = partsA[n];
shorterResult = NSOrderedAscending;
largerResult = NSOrderedDescending;
} else {
missingPart = partsB[n];
shorterResult = NSOrderedDescending;
largerResult = NSOrderedAscending;
}
missingType = [self typeOfCharacter:missingPart];
// Check the type
if (missingType == kStringType) {
// It's a string. Shorter version wins
return shorterResult;
} else {
// It's a number/period. Larger version wins
return largerResult;
}
}
// The 2 strings are identical
return NSOrderedSame;
}
@end

Some files were not shown because too many files have changed in this diff Show More