diff options
author | Tobias Markmann <tm@ayena.de> | 2017-04-26 14:47:38 (GMT) |
---|---|---|
committer | Tobias Markmann <tm@ayena.de> | 2017-05-02 10:21:26 (GMT) |
commit | 29ad7d96951a43af0aa51b769a21c624c5b24c97 (patch) | |
tree | d9c21fe1fa71e44ca2c7e69ddd2de3024f466a58 /SwifTools/AutoUpdater | |
parent | f932894082acc07549f8a0ed2b68ddd18036a220 (diff) | |
download | swift-29ad7d96951a43af0aa51b769a21c624c5b24c97.zip swift-29ad7d96951a43af0aa51b769a21c624c5b24c97.tar.bz2 |
If silent Sparkle update impossible do not auto download updates
With this change, Swift will check at the start if it has
sufficient permissions to write to the location where
Swift is currently installed and also check if it can change
permissions of a temporary file to the permissions of the
current Swift installation.
This should prevent an authentication dialog on the exit of
Swift which does not leave a single clue to what application
the dialog belongs to.
Test-Information:
Verified that the log output indicates a possible silent update
for admin users and the impossibility of a silent update
for non-admin users and a Swift installed to /Applications
folder.
All unit tests pass on macOS 10.12.4 with Qt 5.4.2.
Change-Id: I5f03358ac67630565b3c624da157b1eeea14356d
Diffstat (limited to 'SwifTools/AutoUpdater')
-rw-r--r-- | SwifTools/AutoUpdater/AutoUpdater.h | 1 | ||||
-rw-r--r-- | SwifTools/AutoUpdater/SparkleAutoUpdater.h | 9 | ||||
-rw-r--r-- | SwifTools/AutoUpdater/SparkleAutoUpdater.mm | 90 |
3 files changed, 95 insertions, 5 deletions
diff --git a/SwifTools/AutoUpdater/AutoUpdater.h b/SwifTools/AutoUpdater/AutoUpdater.h index a125229..fdd3a91 100644 --- a/SwifTools/AutoUpdater/AutoUpdater.h +++ b/SwifTools/AutoUpdater/AutoUpdater.h @@ -28,6 +28,7 @@ namespace Swift { virtual void setAppcastFeed(const std::string& appcastFeed) = 0; virtual void checkForUpdates() = 0; virtual State getCurrentState() = 0; + virtual bool applicationInstallationLocationWritable() = 0; public: /** diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.h b/SwifTools/AutoUpdater/SparkleAutoUpdater.h index 48b75e5..26e08da 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.h +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.h @@ -21,11 +21,12 @@ namespace Swift { class SparkleAutoUpdater : public AutoUpdater { public: SparkleAutoUpdater(const std::string& appcastFeed); - ~SparkleAutoUpdater(); + ~SparkleAutoUpdater() override; - void setAppcastFeed(const std::string& appcastFeed); - void checkForUpdates(); - State getCurrentState(); + void setAppcastFeed(const std::string& appcastFeed) override; + void checkForUpdates() override; + State getCurrentState() override; + bool applicationInstallationLocationWritable() override; private: void setCurrentState(State updatedState); diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm index 4cf5837..b4a4c05 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm @@ -6,9 +6,15 @@ #include <SwifTools/AutoUpdater/SparkleAutoUpdater.h> + +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + #include <Cocoa/Cocoa.h> #include <Sparkle/Sparkle.h> +#include <Swiften/Base/Log.h> + #include <SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h> #include <SwifTools/Cocoa/CocoaUtil.h> @@ -34,7 +40,13 @@ SparkleAutoUpdater::SparkleAutoUpdater(const std::string& appcastFeed) : d(new P [d->updater setAutomaticallyChecksForUpdates: true]; // Automatically check for an update after a day. [d->updater setUpdateCheckInterval: 86400]; - [d->updater setAutomaticallyDownloadsUpdates: true]; + + auto canDoSilentUpdates = applicationInstallationLocationWritable(); + [d->updater setAutomaticallyDownloadsUpdates: canDoSilentUpdates]; + + SWIFT_LOG(debug) << (canDoSilentUpdates ? + "The current running user has enough permissions to do a silent update." : + "The current running user has insufficient permissions to do a silent update.") << std::endl; setAppcastFeed(appcastFeed); } @@ -67,4 +79,80 @@ AutoUpdater::State SparkleAutoUpdater::getCurrentState() { return d->currentState; } +bool Swift::SparkleAutoUpdater::applicationInstallationLocationWritable() { + auto bundlePath = ns2StdString([[NSBundle mainBundle] bundlePath]); + + auto createTemporaryFile = [](const boost::filesystem::path& parentFolder) { + boost::optional<boost::filesystem::path> tempFilePath; + boost::system::error_code error; + + if (!boost::filesystem::is_directory(parentFolder, error) || error) { + return tempFilePath; + } + auto uniquePath = boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%", error); + if (error) { + return tempFilePath; + } + auto testFilePath = parentFolder / uniquePath; + + boost::filesystem::ofstream testFile(testFilePath, std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc); + if (testFile) { + testFile.close(); + tempFilePath = testFilePath; + } + + return tempFilePath; + }; + + auto isDirectoryWritable = [&](const boost::filesystem::path& path) { + auto bundleTestFilePath = createTemporaryFile(path); + if (!bundleTestFilePath) { + return false; + } + + boost::system::error_code error; + if (!boost::filesystem::remove(bundleTestFilePath.get(), error) || error) { + // File did not exist in the first place or error while removing it. + return false; + } + + return true; + }; + + auto applyMatchingPermissions = [](const boost::filesystem::path& permissionsFrom, const boost::filesystem::path& applyTo) { + auto permissions = boost::filesystem::status(permissionsFrom).permissions(); + + boost::system::error_code error; + boost::filesystem::permissions(applyTo, permissions, error); + + return !error; + }; + + auto canChangePermissionsOnTemporaryFile = [&](const boost::filesystem::path& pathToCreateTemporaryFileUnder, const boost::filesystem::path& pathToTakePermissionsFrom) { + auto temporaryFilePath = createTemporaryFile(pathToCreateTemporaryFileUnder); + if (!temporaryFilePath) { + return false; + } + + boost::system::error_code error; + auto fileExists = boost::filesystem::exists(temporaryFilePath.get(), error); + if (!fileExists || error) { + return false; + } + + auto successfullPermissionCopy = applyMatchingPermissions(pathToTakePermissionsFrom, temporaryFilePath.get()); + + boost::filesystem::remove(temporaryFilePath.get(), error); + + return successfullPermissionCopy; + }; + + auto bundleBoostPath = boost::filesystem::path(bundlePath); + if (!isDirectoryWritable(bundleBoostPath.parent_path()) || !isDirectoryWritable(bundleBoostPath)) { + return false; + } + + return canChangePermissionsOnTemporaryFile(bundleBoostPath.parent_path(), bundleBoostPath); +} + } |