macOS: Add support for automatic updates using Sparkle.

Add support for automatic updates using the Sparkle framework. Add
FindSparkle.cmake and associated CMake plumbing. Add a public key and
other info to Info.plist.in. Add ui/macosx/sparkle_bridge.{h,m}, which
wraps the Sparkle API. Make code that's specific to WinSparkle
Windows-only.

Add Sparkle installation steps to the macos-setup scripts. Sparkle
prints a warning if your bundle is unsigned (which is the case during
development) so disable installing it by default.

Updating here takes a long time. We might be able to fix that by
shipping our DSYMs separately.

Change-Id: I6cc6671db5657dadc514bda6bf6e1c8bbc9468a5
Reviewed-on: https://code.wireshark.org/review/35090
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2019-11-14 08:08:28 -08:00
parent fd4bb1e392
commit 02057200fd
18 changed files with 254 additions and 42 deletions

View File

@ -1089,6 +1089,7 @@ if(BUILD_wireshark)
)
if(APPLE)
list(APPEND QT_PACKAGELIST Qt5MacExtras)
ws_find_package(Sparkle ENABLE_SPARKLE HAVE_SOFTWARE_UPDATE)
endif()
if(WIN32)
list(APPEND QT_PACKAGELIST Qt5WinExtras)
@ -1651,6 +1652,7 @@ endif()
feature_summary(WHAT ALL)
# Should this be part of libui?
if(WIN32)
set(PLATFORM_UI_SRC
ui/win32/console_win32.c
@ -1663,6 +1665,9 @@ elseif(APPLE)
set(PLATFORM_UI_SRC
ui/macosx/cocoa_bridge.mm
)
if (SPARKLE_FOUND)
list(APPEND PLATFORM_UI_SRC ui/macosx/sparkle_bridge.m)
endif()
endif()
set(TSHARK_TAP_SRC
@ -2266,6 +2271,7 @@ if(BUILD_wireshark AND QT_FOUND)
${APPLE_APPKIT_LIBRARY}
${APPLE_CORE_FOUNDATION_LIBRARY}
${APPLE_SYSTEM_CONFIGURATION_LIBRARY}
${SPARKLE_LIBRARY}
${WIN_WS2_32_LIBRARY}
${WIN_VERSION_LIBRARY}
${WINSPARKLE_LIBRARIES}
@ -2312,6 +2318,7 @@ if(BUILD_wireshark AND QT_FOUND)
endif()
target_link_libraries(wireshark ${wireshark_LIBS})
target_include_directories(wireshark SYSTEM PRIVATE ${SPARKLE_INCLUDE_DIR})
install(
TARGETS wireshark

View File

@ -75,7 +75,7 @@ option(ENABLE_LUA "Build with Lua dissector support" ON)
option(ENABLE_SMI "Build with libsmi snmp support" ON)
option(ENABLE_GNUTLS "Build with RSA decryption support" ON)
if(WIN32)
option(ENABLE_WINSPARKLE "Enable WinSparkle support" ON)
option(ENABLE_WINSPARKLE "Enable automatic updates using WinSparkle" ON)
endif()
if (NOT WIN32)
option(ENABLE_CAP "Build with Posix capabilities support" ON)
@ -105,4 +105,5 @@ endif()
if(APPLE)
option(ENABLE_APPLICATION_BUNDLE "Build a macOS application bundle (Wireshark.app)" ON)
option(ENABLE_SPARKLE "Enable automatic updates using Sparkle" ON)
endif()

View File

@ -0,0 +1,22 @@
#
# Find the Sparkle framework
#
# This defines the following:
# SPARKLE_FOUND - True if we found Sparkle
# SPARKLE_INCLUDE_DIR - Path to Sparkle.h
# SPARKLE_LIBRARY - Path to Sparkle.framework
include(FindPackageHandleStandardArgs)
file(GLOB USR_LOCAL_HINT "/usr/local/Sparkle-[1-9]*/")
file(GLOB HOMEBREW_HINT "/usr/local/Caskroom/sparkle/[1-9]*/")
find_path(SPARKLE_INCLUDE_DIR Sparkle.h
HINTS ${USR_LOCAL_HINT} ${HOMEBREW_HINT}
)
find_library(SPARKLE_LIBRARY NAMES Sparkle
HINTS ${USR_LOCAL_HINT} ${HOMEBREW_HINT}
)
find_package_handle_standard_args(Sparkle DEFAULT_MSG SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)
mark_as_advanced(SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)

View File

@ -290,7 +290,7 @@
/* Define to 1 if you have the `setresuid' function. */
#cmakedefine HAVE_SETRESUID 1
/* Define to 1 if you have the WinSparkle library */
/* Define to 1 if you have the Sparkle or WinSparkle library */
#cmakedefine HAVE_SOFTWARE_UPDATE 1
/* Define if you have the 'strptime' function. */

View File

@ -34,6 +34,7 @@ section below for more details.
The following features are new (or have been significantly updated)
since version 3.1.0:
* Automatic updates are supported on macOS.
* You can now follow HTTP/2 and QUIC streams.
* You can once again mark and unmark packets using the middle mouse button.
This feature went missing around 2009 or so.

View File

@ -241,5 +241,22 @@
-->
<key>LSMinimumSystemVersion</key>
<string>@MIN_MACOS_VERSION@</string>
<!-- Sparkle settings https://sparkle-project.org/documentation/customization/ -->
<key>SUFeedURL</key>
<string>https://www.wireshark.org/update/0/Wireshark/@PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_PATCH_VERSION@/macOS/x86-64/en-US/stable.xml</string>
<key>SUEnableAutomaticChecks</key>
<false/>
<key>SUPublicEDKey</key>
<string>BeSuPpEZOmOzkON9QMnfIdwyi06P/LcVoik8M5O2bsQ=</string>
<key>SUEnableSystemProfiling</key>
<false/>
<!--
Disable automatic updates here, then enable them at program
startup depending on prefs.gui_update_enabled.
-->
<key>SUAutomaticallyUpdate</key>
<false/>
</dict>
</plist>

View File

@ -114,6 +114,8 @@ if [ ! -d "$qt_frameworks_dir" ] ; then
exit 1
fi
sparkle_frameworks_dir="@SPARKLE_LIBRARY@"
#
# Leave the Qt frameworks out of the special processing.
#
@ -228,6 +230,10 @@ fi
#
/usr/bin/install_name_tool -delete_rpath "$qt_frameworks_dir" $pkgexec/Wireshark
if [ -d "$sparkle_frameworks_dir" ] ; then
cp -r "$sparkle_frameworks_dir" "$pkglib" || exit 1
fi
# NOTE: we must rpathify *all* files, *including* Qt libraries etc.,
#
rpathify_file () {

View File

@ -18,6 +18,9 @@ brew install c-ares glib libgcrypt gnutls lua@5.1 cmake python nghttp2 snappy lz
#install Qt5
brew install qt5
# Uncomment to enable automatic updates using Sparkle
#brew cask install sparkle
#
# Editor modelines
#

View File

@ -172,6 +172,8 @@ PYTHON3_VERSION=3.7.1
BROTLI_VERSION=1.0.7
# minizip
ZLIB_VERSION=1.2.11
# Uncomment to enable automatic updates using Sparkle
#SPARKLE_VERSION=1.22.0
#
# Asciidoctor is required to build the documentation.
@ -1836,6 +1838,32 @@ uninstall_minizip() {
fi
}
install_sparkle() {
if [ "$SPARKLE_VERSION" ] && [ ! -f sparkle-$SPARKLE_VERSION-done ] ; then
echo "Downloading and installing Sparkle:"
#
# Download the tarball and unpack it in /usr/local/Sparkle-x.y.z
#
[ -f Sparkle-$SPARKLE_VERSION.tar.bz2 ] || curl -L -o Sparkle-$SPARKLE_VERSION.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/$SPARKLE_VERSION/Sparkle-$SPARKLE_VERSION.tar.bz2 || exit 1
$no_build && echo "Skipping installation" && return
sudo mkdir -f "/usr/local/Sparkle-$SPARKLE_VERSION"
sudo tar -C "/usr/local/Sparkle-$SPARKLE_VERSION" -xf Sparkle-$SPARKLE_VERSION.tar.bz2
touch sparkle-$SPARKLE_VERSION-done
fi
}
uninstall_sparkle() {
if [ -n "$installed_sparkle_version" ]; then
echo "Uninstalling Sparkle:"
sudo rm -rf "/usr/local/Sparkle-$installed_sparkle_version"
if [ "$#" -eq 1 ] && [ "$1" = "-r" ] ; then
rm -f "Sparkle-$installed_sparkle_version.tar.bz2"
fi
installed_sparkle_version=""
fi
}
install_all() {
#
# Check whether the versions we have installed are the versions
@ -2234,6 +2262,17 @@ install_all() {
uninstall_minizip -r
fi
if [ ! -z "$installed_sparkle_version" -a \
"$installed_sparkle_version" != "$SPARKLE_VERSION" ] ; then
echo "Installed Sparkle version is $installed_sparkle_version"
if [ -z "$SPARKLE_VERSION" ] ; then
echo "Sparkle is not requested"
else
echo "Requested Sparkle version is $SPARKLE_VERSION"
fi
uninstall_sparkle -r
fi
#
# Start with curl: we may need it to download and install xz.
#
@ -2341,6 +2380,8 @@ install_all() {
install_brotli
install_minizip
install_sparkle
}
uninstall_all() {
@ -2357,6 +2398,8 @@ uninstall_all() {
# We also do a "make distclean", so that we don't have leftovers from
# old configurations.
#
uninstall_sparkle
uninstall_minizip
uninstall_brotli
@ -2568,6 +2611,7 @@ then
installed_python3_version=`ls python3-*-done 2>/dev/null | sed 's/python3-\(.*\)-done/\1/'`
installed_brotli_version=`ls brotli-*-done 2>/dev/null | sed 's/brotli-\(.*\)-done/\1/'`
installed_minizip_version=`ls minizip-*-done 2>/dev/null | sed 's/minizip-\(.*\)-done/\1/'`
installed_sparkle_version=`ls sparkle-*-done 2>/dev/null | sed 's/sparkle-\(.*\)-done/\1/'`
cd $topdir
fi

View File

@ -0,0 +1,38 @@
/* sparkle_bridge.h
*
* C wrapper for the Sparkle API
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
// XXX We could alternatively do this via C++:
// https://github.com/sparkle-project/Sparkle/issues/1137
#ifndef SPARKLE_BRIDGE_H
#define SPARKLE_BRIDGE_H
#include <stdbool.h>
void sparkle_software_update_init(const char *url, bool enabled, int interval);
void sparkle_software_update_check(void);
#endif // SPARKLE_BRIDGE_H
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -0,0 +1,44 @@
/* sparkle_bridge.m
*
* C wrapper for the Sparkle API
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <ui/macosx/sparkle_bridge.h>
#import <Cocoa/Cocoa.h>
#import <Sparkle.h>
// https://sparkle-project.org/documentation/customization/
// Sparkle stores its state in ~/Library/Preferences/org.wireshark.Wireshark.plist
void sparkle_software_update_init(const char *url, bool enabled, int interval)
{
[[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates: enabled];
[[SUUpdater sharedUpdater] setUpdateCheckInterval: interval];
[[SUUpdater sharedUpdater] setFeedURL: [NSURL URLWithString: [[NSString alloc] initWithUTF8String: url] ]];
}
void sparkle_software_update_check(void)
{
[[SUUpdater sharedUpdater] checkForUpdatesInBackground];
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -365,7 +365,7 @@ MainWindow::MainWindow(QWidget *parent) :
connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
updateRecentCaptures();
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
connect(wsApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()),
Qt::BlockingQueuedConnection);
connect(wsApp, SIGNAL(softwareUpdateClose()), this, SLOT(close()),

View File

@ -416,7 +416,7 @@ private slots:
void openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata);
void openTapParameterDialog();
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
void softwareUpdateRequested();
#endif

View File

@ -1642,7 +1642,7 @@ void MainWindow::openTapParameterDialog()
openTapParameterDialog(cfg_str, NULL, NULL);
}
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
void MainWindow::softwareUpdateRequested() {
// We could call testCaptureFileClose here, but that would give us yet
// another dialog. Just try again later.

View File

@ -223,7 +223,7 @@ extern "C" void menu_recent_file_write_all(FILE *rf) {
}
}
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
/** Check to see if Wireshark can shut down safely (e.g. offer to save the
* current capture).
*/
@ -236,7 +236,7 @@ extern "C" int software_update_can_shutdown_callback(void) {
extern "C" void software_update_shutdown_request_callback(void) {
wsApp->softwareUpdateShutdownRequest();
}
#endif // HAVE_SOFTWARE_UPDATE
#endif // HAVE_SOFTWARE_UPDATE && Q_OS_WIN
// Check each recent item in a separate thread so that we don't hang while
// calling stat(). This is called periodically because files and entire
@ -861,7 +861,7 @@ WiresharkApplication::WiresharkApplication(int &argc, char **argv) :
QPalette gui_pal = qApp->palette();
prefs_set_gui_theme_is_dark(gui_pal.windowText().color().value() > gui_pal.window().color().value());
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
connect(this, SIGNAL(softwareUpdateQuit()), this, SLOT(quit()), Qt::QueuedConnection);
#endif
@ -1301,7 +1301,7 @@ void WiresharkApplication::zoomTextFont(int zoomLevel)
emit zoomMonospaceFont(zoomed_font_);
}
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
bool WiresharkApplication::softwareUpdateCanShutdown() {
software_update_ok_ = true;
// At this point the update is ready to install, but WinSparkle has

View File

@ -123,7 +123,7 @@ public:
const QString windowTitleString(QStringList title_parts);
const QString windowTitleString(QString title_part) { return windowTitleString(QStringList() << title_part); }
void applyCustomColorsFromRecent();
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
void rejectSoftwareUpdate() { software_update_ok_ = false; }
bool softwareUpdateCanShutdown();
void softwareUpdateShutdownRequest();
@ -156,7 +156,7 @@ private:
static QString window_title_separator_;
QList<AppSignal> app_signals_;
int active_captures_;
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
bool software_update_ok_;
#endif
@ -194,7 +194,7 @@ signals:
void checkDisplayFilter();
void fieldsChanged();
void reloadLuaPlugins();
#ifdef HAVE_SOFTWARE_UPDATE
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
// Each of these are called from a separate thread.
void softwareUpdateRequested();
void softwareUpdateClose();

View File

@ -20,12 +20,15 @@
* - The schema version (fixed, 0)
* - The application name (fixed, "Wireshark")
* - The application version ("<major>.<minor>.<micro>")
* - The operating system (varable, one of "windows" or "osx")
* - The operating system (varable, one of "Windows" or "macOS")
* - The architecture name (variable, one of "x86", "x86-64")
* - The locale (fixed, "en-US)
* - The update channel (variable, one of "development" or "stable") + .xml
*
* Based on https://wiki.mozilla.org/Software_Update:Checking_For_Updates
*
* To do for version 1:
* - Distinguish between NSIS (.exe) and WiX (.msi) on Windows.
*/
#ifdef HAVE_SOFTWARE_UPDATE
@ -35,28 +38,30 @@
#define SU_LOCALE "en-US"
#endif /* HAVE_SOFTWARE_UPDATE */
#if defined(HAVE_SOFTWARE_UPDATE) && defined (_WIN32)
#ifdef HAVE_SOFTWARE_UPDATE
#include "glib.h"
#ifdef _WIN32
#include <winsparkle.h>
#define SU_OSNAME "Windows"
#elif defined(__APPLE__)
#include <macosx/sparkle_bridge.h>
#define SU_OSNAME "macOS"
#else
#error HAVE_SOFTWARE_UPDATE can only be defined for Windows or macOS.
#endif
static GString *update_url_str = NULL;
// https://sourceforge.net/p/predef/wiki/Architectures/
#if defined(__x86_64__) || defined(_M_X64)
#define SU_ARCH "x86-64"
#elif defined(defined(__i386__) || defined(_M_IX86)
#define SU_ARCH "x86"
#endif
static const char *get_appcast_update_url(software_update_channel_e chan) {
static char *get_appcast_update_url(software_update_channel_e chan) {
GString *update_url_str = g_string_new("");;
const char *chan_name;
const char *arch = "x86";
if (!update_url_str) {
update_url_str = g_string_new("");
}
/* XXX Add WOW64 checks similar to version_info.c? */
if (sizeof(arch) != 4) {
arch = "x86-64";
}
switch (chan) {
case UPDATE_CHANNEL_DEVELOPMENT:
@ -72,11 +77,12 @@ static const char *get_appcast_update_url(software_update_channel_e chan) {
SU_APPLICATION,
VERSION,
SU_OSNAME,
arch,
SU_ARCH,
chan_name);
return update_url_str->str;
return g_string_free(update_url_str, FALSE);
}
#ifdef _WIN32
/** Initialize software updates.
*/
void
@ -115,7 +121,32 @@ extern void software_update_cleanup(void) {
win_sparkle_cleanup();
}
#else /* defined(HAVE_SOFTWARE_UPDATE) && defined (_WIN32) */
#elif defined (__APPLE__)
/** Initialize software updates.
*/
void
software_update_init(void) {
char *update_url = get_appcast_update_url(prefs.gui_update_channel);
sparkle_software_update_init(update_url, prefs.gui_update_enabled, prefs.gui_update_interval);
g_free(update_url);
}
/** Force a software update check.
*/
void
software_update_check(void) {
sparkle_software_update_check();
}
/** Clean up software update checking.
*/
void software_update_cleanup(void) {
}
#endif
#else /* No updates */
/** Initialize software updates.
*/
@ -134,18 +165,6 @@ software_update_check(void) {
void software_update_cleanup(void) {
}
/** Check to see if Wireshark can shut down safely (e.g. offer to save the
* current capture).
*/
int software_update_can_shutdown_callback(void) {
return FALSE;
}
/** Shut down Wireshark in preparation for an upgrade.
*/
void software_update_shutdown_request_callback(void) {
}
#endif /* defined(HAVE_SOFTWARE_UPDATE) && defined (_WIN32) */
/*

View File

@ -11,6 +11,14 @@
#ifndef __SOFTWARE_UPDATE_H__
#define __SOFTWARE_UPDATE_H__
/** @file
* Automatic update routines.
*
* Routines that integrate with WinSparkle on Windows and Sparkle on
* macOS.
* @ingroup main_ui_group
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@ -33,6 +41,7 @@ extern void software_update_check(void);
*/
extern void software_update_cleanup(void);
#ifdef _WIN32
/** Check to see if Wireshark can shut down safely (e.g. offer to save the
* current capture). Called from a separate thread.
*
@ -46,6 +55,7 @@ extern int software_update_can_shutdown_callback(void);
* Does nothing on platforms that don't support software updates.
*/
extern void software_update_shutdown_request_callback(void);
#endif
#ifdef __cplusplus
}