summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'SwifTools/AutoUpdater')
-rw-r--r--SwifTools/AutoUpdater/AutoUpdater.cpp6
-rw-r--r--SwifTools/AutoUpdater/AutoUpdater.h41
-rw-r--r--SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp16
-rw-r--r--SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h18
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.h40
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.mm150
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h37
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm79
8 files changed, 333 insertions, 54 deletions
diff --git a/SwifTools/AutoUpdater/AutoUpdater.cpp b/SwifTools/AutoUpdater/AutoUpdater.cpp
index e424f3b..342b1d9 100644
--- a/SwifTools/AutoUpdater/AutoUpdater.cpp
+++ b/SwifTools/AutoUpdater/AutoUpdater.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <SwifTools/AutoUpdater/AutoUpdater.h>
diff --git a/SwifTools/AutoUpdater/AutoUpdater.h b/SwifTools/AutoUpdater/AutoUpdater.h
index 77e0045..fdd3a91 100644
--- a/SwifTools/AutoUpdater/AutoUpdater.h
+++ b/SwifTools/AutoUpdater/AutoUpdater.h
@@ -1,16 +1,41 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <string>
+
+#include <boost/signals2.hpp>
+
namespace Swift {
- class AutoUpdater {
- public:
- virtual ~AutoUpdater();
+ class AutoUpdater {
+ public:
+ enum class State {
+ NotCheckedForUpdatesYet,
+ NoUpdateAvailable,
+ CheckingForUpdate,
+ ErrorCheckingForUpdate,
+ DownloadingUpdate,
+ RestartToInstallUpdate
+ };
+
+ public:
+ virtual ~AutoUpdater();
+
+ virtual void setAppcastFeed(const std::string& appcastFeed) = 0;
+ virtual void checkForUpdates() = 0;
+ virtual State getCurrentState() = 0;
+ virtual bool applicationInstallationLocationWritable() = 0;
- virtual void checkForUpdates() = 0;
- };
+ public:
+ /**
+ * Emit this signal if a new version of the software has been downloaded
+ * and the user needs to be notified so they can quit the app and start
+ * the newer version.
+ */
+ boost::signals2::signal<void(State)> onUpdateStateChanged;
+ };
}
diff --git a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp
index d2e89ac..9ae8c09 100644
--- a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp
+++ b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>
@@ -16,18 +16,18 @@ namespace Swift {
bool PlatformAutoUpdaterFactory::isSupported() const {
#ifdef HAVE_SPARKLE
- return true;
+ return true;
#else
- return false;
+ return false;
#endif
}
AutoUpdater* PlatformAutoUpdaterFactory::createAutoUpdater(const std::string& appcastURL) {
#ifdef HAVE_SPARKLE
- return new SparkleAutoUpdater(appcastURL);
+ return new SparkleAutoUpdater(appcastURL);
#else
- (void) appcastURL;
- return NULL;
+ (void) appcastURL;
+ return nullptr;
#endif
}
diff --git a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h
index 59df238..9942d6a 100644
--- a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h
+++ b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h
@@ -1,18 +1,18 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <string>
namespace Swift {
- class AutoUpdater;
+ class AutoUpdater;
- class PlatformAutoUpdaterFactory {
- public:
- bool isSupported() const;
+ class PlatformAutoUpdaterFactory {
+ public:
+ bool isSupported() const;
- AutoUpdater* createAutoUpdater(const std::string& appcastURL);
- };
+ AutoUpdater* createAutoUpdater(const std::string& appcastURL);
+ };
}
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.h b/SwifTools/AutoUpdater/SparkleAutoUpdater.h
index f367945..26e08da 100644
--- a/SwifTools/AutoUpdater/SparkleAutoUpdater.h
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.h
@@ -1,24 +1,38 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <memory>
#include <string>
+
#include <SwifTools/AutoUpdater/AutoUpdater.h>
namespace Swift {
- class SparkleAutoUpdater : public AutoUpdater {
- public:
- SparkleAutoUpdater(const std::string& url);
- ~SparkleAutoUpdater();
+ /**
+ * @brief The SparkleAutoUpdater class provides integration with Sparkle.
+ * This enables automatic silent background updates. If using this in Qt you
+ * need to emit a NSApplicationWillTerminateNotification before you quit
+ * the application.
+ */
+ class SparkleAutoUpdater : public AutoUpdater {
+ public:
+ SparkleAutoUpdater(const std::string& appcastFeed);
+ ~SparkleAutoUpdater() override;
+
+ void setAppcastFeed(const std::string& appcastFeed) override;
+ void checkForUpdates() override;
+ State getCurrentState() override;
+ bool applicationInstallationLocationWritable() override;
+
+ private:
+ void setCurrentState(State updatedState);
- void checkForUpdates();
-
- private:
- class Private;
- Private* d;
- };
+ private:
+ class Private;
+ const std::unique_ptr<Private> d;
+ };
}
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
index c35abc8..274ab3c 100644
--- a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
@@ -1,34 +1,158 @@
+/*
+ * Copyright (c) 2016-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#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>
+
namespace Swift {
class SparkleAutoUpdater::Private {
- public:
- SUUpdater* updater;
+ public:
+ SUUpdater* updater;
+ boost::intrusive_ptr<SparkleAutoUpdaterDelegate> delegate;
+ State currentState = State::NotCheckedForUpdatesYet;
};
-SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) {
- d = new Private;
+SparkleAutoUpdater::SparkleAutoUpdater(const std::string& appcastFeed) : d(new Private()) {
+ d->updater = [SUUpdater sharedUpdater];
+ [d->updater retain];
- d->updater = [SUUpdater sharedUpdater];
- [d->updater retain];
- [d->updater setAutomaticallyChecksForUpdates: true];
+ d->delegate = boost::intrusive_ptr<SparkleAutoUpdaterDelegate>([[SparkleAutoUpdaterDelegate alloc] init], false);
+ [d->delegate.get() setOnNewUpdateState: [&](AutoUpdater::State updatedState){
+ setCurrentState(updatedState);
+ }];
+ [d->updater setDelegate: d->delegate.get()];
- NSURL* nsurl = [NSURL URLWithString:
- [NSString stringWithUTF8String: url.c_str()]];
- [d->updater setFeedURL: nsurl];
+ [d->updater setAutomaticallyChecksForUpdates: true];
+ // Automatically check for an update after a day.
+ [d->updater setUpdateCheckInterval: 86400];
+
+ 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.");
+
+ setAppcastFeed(appcastFeed);
}
SparkleAutoUpdater::~SparkleAutoUpdater() {
- [d->updater release];
- delete d;
+ [d->updater release];
+}
+
+void SparkleAutoUpdater::setAppcastFeed(const std::string& appcastFeed) {
+ NSURL* nsurl = [NSURL URLWithString: std2NSString(appcastFeed)];
+ [d->updater setFeedURL: nsurl];
}
void SparkleAutoUpdater::checkForUpdates() {
- [d->updater checkForUpdatesInBackground];
+ if (!(getCurrentState() == State::CheckingForUpdate ||
+ getCurrentState() == State::DownloadingUpdate ||
+ getCurrentState() == State::RestartToInstallUpdate)) {
+ setCurrentState(State::CheckingForUpdate);
+ [d->updater resetUpdateCycle];
+ [d->updater checkForUpdatesInBackground];
+ }
+}
+
+void SparkleAutoUpdater::setCurrentState(AutoUpdater::State updatedState) {
+ d->currentState = updatedState;
+ onUpdateStateChanged(d->currentState);
+}
+
+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);
}
}
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h
new file mode 100644
index 0000000..4aa236b
--- /dev/null
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <functional>
+
+#import <Cocoa/Cocoa.h>
+
+#import <Sparkle/Sparkle.h>
+
+#include <SwifTools/AutoUpdater/AutoUpdater.h>
+
+@interface SparkleAutoUpdaterDelegate : NSObject<SUUpdaterDelegate>
+@property (atomic) std::function< void (Swift::AutoUpdater::State)> onNewUpdateState;
+
+- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast;
+
+- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update;
+
+- (id <SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater;
+
+- (void)updaterDidNotFindUpdate:(SUUpdater *)update;
+
+- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request;
+
+- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error;
+
+- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update;
+
+- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation;
+
+- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error;
+@end
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm
new file mode 100644
index 0000000..b9294d9
--- /dev/null
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#import <SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h>
+
+#include <string>
+
+#include <Swiften/Base/Log.h>
+
+#include <SwifTools/Cocoa/CocoaUtil.h>
+
+using namespace Swift;
+
+@implementation SparkleAutoUpdaterDelegate
+
+@synthesize onNewUpdateState;
+
+- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast {
+ (void)updater;
+ (void)appcast;
+ onNewUpdateState(AutoUpdater::State::DownloadingUpdate);
+}
+
+- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update {
+ (void)updater;
+ (void)update;
+}
+
+- (id <SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater {
+ (void)updater;
+ return nil;
+}
+
+- (void)updaterDidNotFindUpdate:(SUUpdater *)updater {
+ (void)updater;
+ onNewUpdateState(AutoUpdater::State::NoUpdateAvailable);
+}
+
+- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request {
+ (void)updater;
+ (void)item;
+ (void)request;
+ onNewUpdateState(AutoUpdater::State::DownloadingUpdate);
+}
+
+- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error {
+ (void)updater;
+ (void)item;
+ SWIFT_LOG(error) << ns2StdString([error localizedDescription]);
+ onNewUpdateState(AutoUpdater::State::ErrorCheckingForUpdate);
+}
+
+- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update {
+ (void)updater;
+ (void)update;
+}
+
+- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation {
+ (void)updater;
+ (void)item;
+ (void)invocation;
+ onNewUpdateState(AutoUpdater::State::RestartToInstallUpdate);
+}
+
+- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error {
+ (void)updater;
+ if ([error code] == SUNoUpdateError) {
+ onNewUpdateState(AutoUpdater::State::NoUpdateAvailable);
+ }
+ else {
+ SWIFT_LOG(error) << ns2StdString([error localizedDescription]);
+ onNewUpdateState(AutoUpdater::State::ErrorCheckingForUpdate);
+ }
+}
+
+@end