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 /SwifTools/AutoUpdater/SparkleAutoUpdater.mm
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
Diffstat (limited to 'SwifTools/AutoUpdater/SparkleAutoUpdater.mm')
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.mm90
1 files changed, 89 insertions, 1 deletions
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);
+}
+
}