summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BuildTools/SCons/SConscript.boot3
-rw-r--r--BuildTools/SCons/SConstruct9
-rw-r--r--BuildTools/SCons/Tools/AppBundle.py7
-rw-r--r--DEVELOPMENT.md19
-rw-r--r--SwifTools/AutoUpdater/AutoUpdater.h13
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.h7
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdater.mm31
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h35
-rw-r--r--SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm57
-rw-r--r--SwifTools/SConscript2
-rw-r--r--Swift/QtUI/CocoaUIHelpers.h1
-rw-r--r--Swift/QtUI/CocoaUIHelpers.mm4
-rw-r--r--Swift/QtUI/QtSwift.cpp32
-rw-r--r--Swift/QtUI/QtSwift.h8
-rw-r--r--Swift/QtUI/SConscript5
15 files changed, 217 insertions, 16 deletions
diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot
index 8379a58..d6527f3 100644
--- a/BuildTools/SCons/SConscript.boot
+++ b/BuildTools/SCons/SConscript.boot
@@ -110,6 +110,9 @@ vars.Add("codesign_identity", "macOS code signing identity to be passed to codes
vars.Add("signtool_key_pfx", "The keyfile (.pfx) that will be used to sign the Windows installer.", None)
vars.Add("signtool_timestamp_url", "The timestamp server that will be queried for a signed time stamp in the signing process.", None)
+# Automatic Software Update Options
+vars.Add(PathVariable("sparkle_public_dsa_key", "Optional path to a public DSA key used to verify Sparkle software updates. Without specifiying this option, the app needs to be code signed for Sparkle to work.", None, PathVariable.PathIsFile))
+
################################################################################
# Set up default build & configure environment
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct
index b5757b8..2da3787 100644
--- a/BuildTools/SCons/SConstruct
+++ b/BuildTools/SCons/SConstruct
@@ -289,7 +289,7 @@ if env.get("try_gconf", True) and env["PLATFORM"] != "win32" and env["PLATFORM"]
env["HAVE_SPARKLE"] = 0
if env["PLATFORM"] == "darwin" :
sparkle_flags = {
- "FRAMEWORKPATH": ["/Library/Frameworks"],
+ "FRAMEWORKPATH": ["3rdParty/Sparkle/Sparkle-1.14.0"],
"FRAMEWORKS": ["Sparkle"]
}
sparkle_env = conf_env.Clone()
@@ -298,9 +298,14 @@ if env["PLATFORM"] == "darwin" :
if conf.CheckObjCHeader("Sparkle/Sparkle.h") :
env["HAVE_SPARKLE"] = 1
env["SPARKLE_FLAGS"] = sparkle_flags
- env["SPARKLE_FRAMEWORK"] = "/Library/Frameworks/Sparkle.framework"
+ env["SPARKLE_FRAMEWORK"] = Dir("../../3rdParty/Sparkle/Sparkle-1.14.0/Sparkle.framework")
conf.Finish()
+ if env.get("sparkle_public_dsa_key", None) != None :
+ env["SWIFT_SPARKLE_PUBLIC_DSA_KEY"] = File(env.get("sparkle_public_dsa_key"))
+ else :
+ env["SWIFT_SPARKLE_PUBLIC_DSA_KEY"] = None
+
# Growl
env["HAVE_GROWL"] = 0
if env["PLATFORM"] == "darwin" :
diff --git a/BuildTools/SCons/Tools/AppBundle.py b/BuildTools/SCons/Tools/AppBundle.py
index fda3484..5f19898 100644
--- a/BuildTools/SCons/Tools/AppBundle.py
+++ b/BuildTools/SCons/Tools/AppBundle.py
@@ -1,7 +1,7 @@
import SCons.Util, os.path
def generate(env) :
- def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}, handlesXMPPURIs = False) :
+ def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}, handlesXMPPURIs = False, sparklePublicDSAKey = None) :
bundleDir = bundle + ".app"
bundleContentsDir = bundleDir + "/Contents"
resourcesDir = bundleContentsDir + "/Resources"
@@ -44,6 +44,11 @@ def generate(env) :
</array>
</dict>
</array>\n"""
+
+ if sparklePublicDSAKey :
+ plist += "<key>SUPublicDSAKeyFile</key>"
+ plist += "<string>" + sparklePublicDSAKey.name.encode("utf-8") + "</string>"
+ env.Install(resourcesDir, sparklePublicDSAKey)
plist += """</dict>
</plist>
"""
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 2b8ca99..3d9c1a7 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -68,6 +68,25 @@ Notes:
paths
- Currently only 32-bit builds of the Swift client are supported
+## Automatic Software Updates
+Automatic software updates allow distribution of updates directly to the end users.
+This is useful for general feature updates, bug fixes and especially for security
+updates.
+
+### Automatic Software Updates for Mac OS X using Sparkle
+Swift supports integration with the software update framework [Sparkle](https://sparkle-project.org/) on OS X. For security reasons,
+Sparkle requires the application to be either code-signed or a bundled public DSA
+key. In case you do not code-sign, you can provide the path to the public DSA key
+to be bundled with the application bundle via the `sparkle_public_dsa_key` SCons
+argument.
+
+To build with Sparkle support, simply download Sparkle-1.14.0 and extract it to
+`3rdParty/Sparkle/Sparkle-1.14.0`. SCons will pick it up during configuration
+and build Swift with Sparkle support.
+
+The appcast URL is specified as a compile time preprocessor variable `SWIFT_APPCAST_URL`
+in `Swift/QtUI/QtSwift.cpp`
+
## Building Swiften for Android
This section describes how to build Swiften for Android. It can then be used from any Android native code. This guide has been tested on OS X and Linux.
diff --git a/SwifTools/AutoUpdater/AutoUpdater.h b/SwifTools/AutoUpdater/AutoUpdater.h
index dec85c9..ed53e11 100644
--- a/SwifTools/AutoUpdater/AutoUpdater.h
+++ b/SwifTools/AutoUpdater/AutoUpdater.h
@@ -1,16 +1,27 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <boost/signals2.hpp>
+
namespace Swift {
class AutoUpdater {
public:
virtual ~AutoUpdater();
virtual void checkForUpdates() = 0;
+ virtual bool recommendRestartToUpdate() = 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()> onSuggestRestartToUserToUpdate;
};
}
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.h b/SwifTools/AutoUpdater/SparkleAutoUpdater.h
index 95ca35e..c3394f7 100644
--- a/SwifTools/AutoUpdater/SparkleAutoUpdater.h
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.h
@@ -11,12 +11,19 @@
#include <SwifTools/AutoUpdater/AutoUpdater.h>
namespace Swift {
+ /**
+ * @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& url);
~SparkleAutoUpdater();
void checkForUpdates();
+ bool recommendRestartToUpdate();
private:
class Private;
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
index bcd1388..7e06b2f 100644
--- a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm
@@ -1,13 +1,24 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#include <SwifTools/AutoUpdater/SparkleAutoUpdater.h>
#include <Cocoa/Cocoa.h>
#include <Sparkle/Sparkle.h>
+#include <SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h>
+#include <SwifTools/Cocoa/CocoaUtil.h>
+
namespace Swift {
class SparkleAutoUpdater::Private {
public:
SUUpdater* updater;
+ boost::intrusive_ptr<SparkleAutoUpdaterDelegate> delegate;
+ bool restartToUpdate = false;
};
SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) {
@@ -15,20 +26,36 @@ SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) {
d->updater = [SUUpdater sharedUpdater];
[d->updater retain];
+
+ d->delegate = boost::intrusive_ptr<SparkleAutoUpdaterDelegate>([[SparkleAutoUpdaterDelegate alloc] init], false);
+ [d->delegate.get() setUpdateDownloadFinished: [&](){
+ d->restartToUpdate = true;
+ onSuggestRestartToUserToUpdate();
+ }];
+ [d->updater setDelegate: d->delegate.get()];
+
[d->updater setAutomaticallyChecksForUpdates: true];
+ // Automatically check for an update after a day.
+ [d->updater setUpdateCheckInterval: 86400];
+ [d->updater setAutomaticallyDownloadsUpdates: true];
- NSURL* nsurl = [NSURL URLWithString:
- [NSString stringWithUTF8String: url.c_str()]];
+ NSURL* nsurl = [NSURL URLWithString: std2NSString(url)];
[d->updater setFeedURL: nsurl];
}
SparkleAutoUpdater::~SparkleAutoUpdater() {
[d->updater release];
delete d;
+ SWIFT_LOG(warning) << std::endl;
}
void SparkleAutoUpdater::checkForUpdates() {
+ //[d->updater resetUpdateCycle]; // This is useful for testing to force a check ot start.
[d->updater checkForUpdatesInBackground];
}
+bool SparkleAutoUpdater::recommendRestartToUpdate() {
+ return d->restartToUpdate;
+}
+
}
diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h
new file mode 100644
index 0000000..8f408de
--- /dev/null
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <functional>
+
+#import <Cocoa/Cocoa.h>
+
+#import <Sparkle/Sparkle.h>
+
+namespace Swift {
+ class SparkleAutoUpdater;
+}
+
+@interface SparkleAutoUpdaterDelegate : NSObject<SUUpdaterDelegate>
+@property (atomic) std::function< void ()> updateDownloadFinished;
+
+- (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 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..6e832ba
--- /dev/null
+++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 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 updateDownloadFinished;
+
+- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast {
+ (void)updater;
+ (void)appcast;
+}
+
+- (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;
+}
+
+- (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;
+ updateDownloadFinished();
+}
+
+- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error {
+ (void)updater;
+ SWIFT_LOG(error) << ns2StdString([error localizedDescription]) << std::endl;
+}
+
+@end
diff --git a/SwifTools/SConscript b/SwifTools/SConscript
index aa6d47e..dec343e 100644
--- a/SwifTools/SConscript
+++ b/SwifTools/SConscript
@@ -50,7 +50,7 @@ if env["SCONS_STAGE"] == "build" :
if swiftools_env.get("HAVE_SPARKLE", 0) :
swiftools_env.UseFlags(swiftools_env["SPARKLE_FLAGS"])
swiftools_env.Append(CPPDEFINES = ["HAVE_SPARKLE"])
- sources += ["AutoUpdater/SparkleAutoUpdater.mm"]
+ sources += ["AutoUpdater/SparkleAutoUpdater.mm", "AutoUpdater/SparkleAutoUpdaterDelegate.mm"]
if swiftools_env["PLATFORM"] == "win32" :
sources += ["Idle/WindowsIdleQuerier.cpp"]
diff --git a/Swift/QtUI/CocoaUIHelpers.h b/Swift/QtUI/CocoaUIHelpers.h
index 58cd539..8d96bd9 100644
--- a/Swift/QtUI/CocoaUIHelpers.h
+++ b/Swift/QtUI/CocoaUIHelpers.h
@@ -21,6 +21,7 @@ namespace Swift {
class CocoaUIHelpers {
public:
static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain);
+ static void sendCocoaApplicationWillTerminateNotification();
};
}
diff --git a/Swift/QtUI/CocoaUIHelpers.mm b/Swift/QtUI/CocoaUIHelpers.mm
index c876312..3ffa72c 100644
--- a/Swift/QtUI/CocoaUIHelpers.mm
+++ b/Swift/QtUI/CocoaUIHelpers.mm
@@ -46,4 +46,8 @@ void CocoaUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::
[certificates release];
}
+void CocoaUIHelpers::sendCocoaApplicationWillTerminateNotification() {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"NSApplicationWillTerminateNotification" object:nil];
+}
+
}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 5d05a3d..d8dfac4 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -77,12 +77,16 @@
#include <Swift/QtUI/QtDBUSURIHandler.h>
#endif
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include <Swift/QtUI/CocoaUIHelpers.h>
+#endif
+
namespace Swift{
#if defined(SWIFTEN_PLATFORM_MACOSX)
-//#define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml"
+#define SWIFT_APPCAST_URL "https://swift.im/appcast/swift-mac-dev.xml"
#else
-//#define SWIFT_APPCAST_URL ""
+#define SWIFT_APPCAST_URL ""
#endif
po::options_description QtSwift::getOptionsDescription() {
@@ -277,12 +281,14 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
mainControllers_.push_back(mainController);
}
+ connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(handleAboutToQuit()));
- // PlatformAutoUpdaterFactory autoUpdaterFactory;
- // if (autoUpdaterFactory.isSupported()) {
- // autoUpdater_ = autoUpdaterFactory.createAutoUpdater(SWIFT_APPCAST_URL);
- // autoUpdater_->checkForUpdates();
- // }
+ PlatformAutoUpdaterFactory autoUpdaterFactory;
+ if (autoUpdaterFactory.isSupported()) {
+ autoUpdater_ = autoUpdaterFactory.createAutoUpdater(SWIFT_APPCAST_URL);
+ autoUpdater_->checkForUpdates();
+ autoUpdater_->onSuggestRestartToUserToUpdate.connect(boost::bind(&QtSwift::handleRecommendRestartToInstallUpdate, this));
+ }
}
QtSwift::~QtSwift() {
@@ -312,4 +318,16 @@ QtSwift::~QtSwift() {
delete applicationPathProvider_;
}
+void QtSwift::handleAboutToQuit() {
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+ // This is required so Sparkle knows about the application shutting down
+ // and can update the application in background.
+ CocoaUIHelpers::sendCocoaApplicationWillTerminateNotification();
+#endif
+}
+
+void QtSwift::handleRecommendRestartToInstallUpdate() {
+ notifier_->showMessage(Notifier::SystemMessage, Q2PSTRING(tr("Swift Update Available")), Q2PSTRING(tr("Restart Swift now or later to update to the new Swift version")), "", [](){});
+}
+
}
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
index 9932545..64b79b8 100644
--- a/Swift/QtUI/QtSwift.h
+++ b/Swift/QtUI/QtSwift.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -59,9 +59,15 @@ namespace Swift {
QtSwift(const po::variables_map& options);
static po::options_description getOptionsDescription();
~QtSwift();
+
+ private slots:
+ void handleAboutToQuit();
+ void handleRecommendRestartToInstallUpdate();
+
private:
XMLSettingsProvider* loadSettingsFile(const QString& fileName);
void loadEmoticonsFile(const QString& fileName, std::map<std::string, std::string>& emoticons);
+
private:
QtEventLoop clientMainThreadCaller_;
PlatformTLSFactories tlsFactories_;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 2d01672..fd47dd4 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -79,6 +79,9 @@ if env["PLATFORM"] == "win32" :
myenv.Append(LIBS = "Cryptui")
myenv.Append(CPPDEFINES = "HAVE_SCHANNEL")
+if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] :
+ myenv.Append(LINKFLAGS = ["-Wl,-rpath,@loader_path/../Frameworks"])
+
myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateQRCTheme(myenv.Dir("#/Swift/resources/themes/Default"), "Default")))
sources = [
@@ -364,7 +367,7 @@ if env["PLATFORM"] == "darwin" :
if env["HAVE_GROWL"] :
frameworks.append(env["GROWL_FRAMEWORK"])
commonResources[""] = commonResources.get("", []) + ["#/Swift/resources/MacOSX/Swift.icns"]
- app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True)
+ app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True, sparklePublicDSAKey = myenv["SWIFT_SPARKLE_PUBLIC_DSA_KEY"])
if env["DIST"] :
myenv.Command(["#/Packages/Swift/Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR " + "\"$CODE_SIGN_IDENTITY\""])
dsym = myenv.Command(["Swift-${SWIFT_VERSION}.dSYM"], ["Swift"], ["dsymutil -o ${TARGET} ${SOURCE}"])