summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2017-04-26 14:47:38 (GMT)
committerTobias Markmann <tm@ayena.de>2017-05-02 10:21:26 (GMT)
commit29ad7d96951a43af0aa51b769a21c624c5b24c97 (patch)
treed9c21fe1fa71e44ca2c7e69ddd2de3024f466a58
parentf932894082acc07549f8a0ed2b68ddd18036a220 (diff)
downloadswift-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
-rw-r--r--SwifTools/AutoUpdater/AutoUpdater.h1
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.h9
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.mm90
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);
+}
+
}