1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-04-21 21:52:03 +02:00

updater: add sparkle

- add sparkle submodule
- add an option to enable sparkle
- modify entitlements
- cleanup Info.plist

Gitlab: #578
Change-Id: I7f562112a72a33e008ab316479fbaa68dc0e07f1
This commit is contained in:
kkostiuk 2022-01-25 11:56:34 -05:00 committed by Andreas Traczyk
parent 6b7c25054d
commit 298493169c
16 changed files with 505 additions and 193 deletions

4
.gitmodules vendored
View file

@ -2,3 +2,7 @@
path = 3rdparty/qrencode-win32
url = https://github.com/BlueDragon747/qrencode-win32.git
ignore = dirty
[submodule "sparkle/Sparkle"]
path = sparkle/Sparkle
url = https://github.com/sparkle-project/Sparkle.git
ignore = dirty

View file

@ -98,7 +98,6 @@ set(COMMON_SOURCES
${SRC_DIR}/accountlistmodel.cpp
${SRC_DIR}/networkmanager.cpp
${SRC_DIR}/instancemanager.cpp
${SRC_DIR}/updatemanager.cpp
${SRC_DIR}/main.cpp
${SRC_DIR}/smartlistmodel.cpp
${SRC_DIR}/utils.cpp
@ -214,7 +213,9 @@ if(MSVC)
)
list(APPEND COMMON_SOURCES
${SRC_DIR}/connectivitymonitor.cpp)
${SRC_DIR}/connectivitymonitor.cpp
${SRC_DIR}/updatemanager.cpp
)
# preprocessor defines
add_definitions(-DUNICODE -DQT_NO_DEBUG -DNDEBUG)
@ -269,7 +270,8 @@ elseif (NOT APPLE)
list(APPEND COMMON_SOURCES
${SRC_DIR}/xrectsel.c
${SRC_DIR}/dbuserrorhandler.cpp
${SRC_DIR}/connectivitymonitor.cpp)
${SRC_DIR}/connectivitymonitor.cpp
${SRC_DIR}/updatemanager.cpp)
list(APPEND COMMON_HEADERS
${SRC_DIR}/dbuserrorhandler.h
${SRC_DIR}/xrectsel.h)
@ -348,10 +350,10 @@ elseif (NOT APPLE)
find_library(ringclient ringclient ${LRCLIBDIR} NO_DEFAULT_PATH)
find_library(qrencode qrencode)
find_library(X11 X11)
else()
else() # APPLE
list(APPEND COMMON_SOURCES
${SRC_DIR}/connectivitymonitor.mm)
find_package(PkgConfig REQUIRED)
${SRC_DIR}/os/macos/updatemanager.mm
${SRC_DIR}/os/macos/connectivitymonitor.mm)
if(NOT DEFINED LRC)
if(EXISTS ${PROJECT_SOURCE_DIR}/../install/lrc)
set(LRC ${PROJECT_SOURCE_DIR}/../install/lrc)
@ -388,9 +390,24 @@ else()
find_library(ringclient ringclient ${LRCLIBDIR} NO_DEFAULT_PATH)
find_library(SYSTEM_CONFIGURATUION SystemConfiguration)
SET(myApp_ICON ${CMAKE_CURRENT_SOURCE_DIR}/resources/images/jami.icns)
SET_SOURCE_FILES_PROPERTIES(${myApp_ICON} PROPERTIES
set(myApp_ICON ${CMAKE_CURRENT_SOURCE_DIR}/resources/images/jami.icns)
set_source_files_properties(${myApp_ICON} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
if(ENABLE_SPARKLE)
message("Sparkle auto-update enabled")
find_library(SPARKLE_FRAMEWORK
NAMES Sparkle
HINTS ${CMAKE_CURRENT_SOURCE_DIR}/sparkle)
add_definitions(-DENABLE_SPARKLE)
message("Sparkle is here:" ${SPARKLE_FRAMEWORK})
set(PUBLIC_KEY_PATH "${CMAKE_CURRENT_SOURCE_DIR}/sparkle/dsa_pub.pem")
set_source_files_properties(${PUBLIC_KEY_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(PUBLIC_KEY ${PUBLIC_KEY_PATH})
endif()
if(BETA)
message(STATUS "Beta config enabled")
add_definitions(-DBETA)
endif()
endif()
# Qt find package
@ -607,12 +624,14 @@ elseif (NOT APPLE)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
else()
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/resources/images/jami.icns)
target_link_libraries(${PROJECT_NAME} PRIVATE
${QT_LIBS}
${LRC_LIB_NAME}
${SYSTEM_CONFIGURATUION}
qrencode)
set(resources ${CMAKE_CURRENT_SOURCE_DIR}/resources/images/jami.icns)
set(libs ${QT_LIBS} ${LRC_LIB_NAME} ${SYSTEM_CONFIGURATUION} qrencode)
if(ENABLE_SPARKLE)
set(resources ${resources} ${PUBLIC_KEY} ${SPARKLE_FRAMEWORK})
set(libs ${libs} ${SPARKLE_FRAMEWORK})
endif(ENABLE_SPARKLE)
target_sources(${PROJECT_NAME} PRIVATE ${resources})
target_link_libraries(${PROJECT_NAME} PRIVATE ${libs})
# translations
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
@ -636,21 +655,23 @@ else()
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/libringclient/translations)
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist"
MACOSX_BUNDLE_EXECUTABLE_NAME "${PROJ_NAME}"
MACOSX_BUNDLE_ICON_FILE "jami.icns"
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUNDLE_ID}"
MACOSX_BUNDLE_BUNDLE_NAME "${PROJ_NAME}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${JAMI_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${JAMI_BUILD}"
MACOSX_BUNDLE_COPYRIGHT "${PROJ_COPYRIGHT}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist"
MACOSX_BUNDLE_EXECUTABLE_NAME "${PROJ_NAME}"
MACOSX_BUNDLE_ICON_FILE "jami.icns"
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUNDLE_ID}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${JAMI_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${JAMI_BUILD}"
MACOSX_BUNDLE_COPYRIGHT "${PROJ_COPYRIGHT}"
SPARKLE_URL "${SPARKLE_URL}"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -DQML_SRC_DIR=${SRC_DIR}
-DMAC_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin
-DEXE_NAME="${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app"
-DSPARKLE_PATH=${SPARKLE_FRAMEWORK}
-DENABLE_SPARKLE=${ENABLE_SPARKLE}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos_qt_deploy.cmake)
endif()

View file

@ -1,5 +1,7 @@
message("Qt deploying in dir " ${QML_SRC_DIR})
execute_process(COMMAND "${MAC_DEPLOY_QT_PATH}/macdeployqt"
message("Qt deploying in dir " ${QML_SRC_DIR})
execute_process(COMMAND "${MAC_DEPLOY_QT_PATH}/macdeployqt"
${EXE_NAME}
-qmldir=${QML_SRC_DIR})
if(${ENABLE_SPARKLE} MATCHES true)
file(COPY ${SPARKLE_PATH} DESTINATION ${EXE_NAME}/Contents/Frameworks/)
endif()

View file

@ -2,39 +2,39 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>10.13</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.social-networking</string>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>NSCameraUsageDescription</key>
<string>Jami requires to access your camera to make calls and record video</string>
<key>NSMicrophoneUsageDescription</key>
<string>Jami requires to access your microphone to make calls and record audio</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Jami requires to access your photo library to show image on profile and send via chat</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>10.13</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.social-networking</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>SUPublicDSAKeyFile</key>
<string>dsa_pub.pem</string>
<key>SUFeedURL</key>
<string>${SPARKLE_URL}</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSCameraUsageDescription</key>
<string>Jami requires to access your camera to make calls and record video</string>
<key>NSMicrophoneUsageDescription</key>
<string>Jami requires to access your microphone to make calls and record audio</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Jami requires to access your photo library to show image on profile and send via chat</string>
</dict>
</plist>

View file

@ -2,13 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.microphone</key>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>

1
sparkle/Sparkle Submodule

@ -0,0 +1 @@
Subproject commit 2195ee0883efc92828a0cf33b830f43f9bd7e01b

36
sparkle/dsa_pub.pem Normal file
View file

@ -0,0 +1,36 @@
-----BEGIN PUBLIC KEY-----
MIIGRzCCBDoGByqGSM44BAEwggQtAoICAQCp4+JqCDyIMIMGtvpMvEPsQJ2SLJrt
y16KsLNmcUXLMMSmHdiC2EEZMhfp4OyuXwLGewA1NXBrBS6+6GidA0hh/IhclMUs
9kjzplVK4mOdKdSvFwuoJ9fdth+ySAXnhpcyLVFKQeoZ/jP20IhW9p+qZE4EMUlx
Pmls+MbNcZLu/HKiGI4XMN2K4yCxLSFjlpEPcT4yBYAZb+YRdY0v2HK3e9Jnja1b
Jfm23NaTRxkWzAu2Cm2S8G7JRo3Uuaw7RUmaAkmVWXFC0ZloGKBSeey6y1EuUtVy
dju3DRVI3RuvmB4yFJvdfgctTR2U6N26H733aOLFsvsSr6/hNp7q0ryDEfjqyW+R
SJwKZIRwl0WTsxwUzw+OejQH9CNcgkRaPgWBntnZ4OWSr2gFPkolt+VpLhSvKiSb
0ef3vZBuTp3KNCDGE20OVfQSeCstUyLZpLeG7tRyJEP/aCni9YTpIhZ5B9XNFe2J
jfzZE2VefKJWpxI1THfPgb0hto6zBuc8kpcKRPqwTRUHQuNwjAuAUKFV3GM9aoUC
KISWXPg2p1z8LgkuM8sgGEhn0BYEfpJFP3wc1OtIlv0t8Bqm1QR1y6hD/uxCYqq+
KR9/0eOsNH7dO/+7ydZjvVcBZ3TeGhvLQB/0Iic4Y895WMvN8bSB7NOZ8ODesO0J
zg2UkMdxdntiKQIhAKISld6gn3g1WSPXvWqT9mZzBly0hXr4DnGI1UtCeQm3AoIC
AQCMiu6knB8mbhcb7bOGhm3JEfi42+j3zavBYOga7LxP18Fobbf+5bHP3kMdNx8y
Paf0q0BkGtRC0WyH0ja05vR0bS9dSUT7qshQXm+/BsA/fnWPC54NcGSfRlj1UqHc
NN39r68EseO7w+w5x1gYFY7Jx/wJqR7gbYgS2GhgIrUo4+vBurl2bVtx6cAwsNXa
h0GUPAGQUu6qJaM5cpZL2Fkx+ac73q9i3WAlCECrkLpvOkLBSbYNvRR1rlhGawGr
Z96zEBEcW5FPJvPsjY2WaOvaRfGF9Y0MK8WXptdxY41jdts7n7kRKuwheUrm0bHm
aCRkGwhtc6hsMdrSzNFLDDScaSjYMx5erqnAKMyieyoiD8gyYN5mhZUokTBdpT1m
n7lrpQ0KfJtNKFtNUfNmU406vMEiTPKG4wxX/RxdzUqLSKNV1j0JHN6kx4Sq/vLN
EzO85ZaA79nBd2/8+ktWRiOuCiLu913Obgw3muNKYNVmH6iJibAYP+n7uUZHCzO4
MxccO5gy1umgTx/16Sya5ov+xt7CmS7kE4M4GzQ+AwXqzx3Mo8O72OWJP7RoRPxt
KTNiNZcjFrPkP4MkAogKNDt3McUXmKzfWEa+EvKHtXav7yiKoZ/kmQCawYQyvKFP
oBloHZ5N2iPnRGfABmFk/exF1Nb2dlhtD1hNYqtD3IWmVAOCAgUAAoICAFSPpbKF
wWcMAwTP7nEWZUr/8efPftwR2Q3F00dbh3ND+Yv7VRam6br+sPnrrPElWL+pPoFy
Vg7qJ6qmsOBgB+dDSiJ5w5L+aIj+vtmQHyCbbLTkCqzC5AO4pMaaXhg5hRQJw6JN
VkLByDsqHmjGG5ZLILzzKLi88X5Tz/Zz5FHWisnwRSGQaoZ5xJOCLfPLTOnASB/Q
uR5nBpYjImZslsPnDwTXVLqqOFo2TiQ3BXGV3BGpP83jaoDSVMjgc2NJNLw7X++b
mEFkALkG9uhhO57dTShwI+S3IzJfIBhSFW59bkY/N0f8peKAiUXmi3M/QWCvfh4k
+WRBaRiq+Ap+wV+IM+PH/INm0uEJ97mP5+7dPMZDNq1iPnJOKhqyXskq6i/Z9eg5
ZzgBw6Pxj6cNhZeg8OQuTfCGIV0m0FtfOZZVUs6l1JlMGb9bGbx2cDJBoI1DQxpG
X01TCtyNF4ShHbFmMG4JLuxBm99YuUJud2wPXToD9pxGWbh7naJwHzL7ywQQ/A0+
gSPE436MLSYPVeGr1RdIxFudZcoGZ2gG6V1aqZfNNlVO++UQ0wNTecFMPhdaC4O/
mnufQC8fSX9qBdnuWfkQQk8bE0kvqz4WSZ+B9Q7bEr7XeOcWibscCslIM2Rs68DK
ZnO5P9x/rPIJLCXY4xQYBryQCMu6JC5ibWzP
-----END PUBLIC KEY-----

11
sparkle/sign_update.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash
set -e
set -o pipefail
if [ "$#" -ne 2 ]; then
echo "Usage: $0 update_archive private_key"
exit 1
fi
openssl=/usr/bin/openssl
$openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | base64 $BASE64_OPTS

46
sparkle/sparkle-xml-updater.sh Executable file
View file

@ -0,0 +1,46 @@
#!/bin/bash
# Take the package to add as argument ./sparkle-xml-updater.sh jami.dmg
REPO_FOLDER=$1
SPARKLE_FILE=$2
REPO_URL=$3
PACKAGE=$4
DSA_KEY=$5
CHANNEL_NAME=$6
VERSION=$7
BUILD=$8
if [ ! -f ${PACKAGE} -o ! -f ${DSA_KEY} ]; then
echo "Can't find package or dsa key, aborting..."
exit 1
fi
if [ -f ${REPO_FOLDER}/${SPARKLE_FILE} ]; then
ITEMS=$(sed -n "/<item>/,/<\/item>/p" ${REPO_FOLDER}/${SPARKLE_FILE})
fi
PACKAGE_SIZE=`stat -f%z ${PACKAGE}`
DATE_RFC2822=`date "+%a, %d %b %Y %T %z"`
cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE}
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>${CHANNEL_NAME}</title>
<link>${REPO_URL}/${SPARKLE_FILE}</link>
<description>Most recent changes with links to updates.</description>
<language>en</language>
<item>
<title>"${CHANNEL_NAME}-${BUILD}"</title>
<pubDate>$DATE_RFC2822</pubDate>
<sparkle:version>${BUILD}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
<enclosure url="${REPO_URL}/$(basename ${PACKAGE})" length="$PACKAGE_SIZE" type="application/octet-stream" sparkle:dsaSignature="$(./sign_update.sh ${PACKAGE} ${DSA_KEY})" />
</item>
$(echo -e "${ITEMS}")
</channel>
</rss>
EOFILE

View file

@ -155,9 +155,11 @@ ApplicationWindow {
windowSettingsLoaded = true
// Quiet check for updates on start if set to.
if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
UpdateManager.checkForUpdates(true)
UpdateManager.setAutoUpdateCheck(true)
if (Qt.platform.os.toString() !== "osx") {
if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
UpdateManager.checkForUpdates(true)
UpdateManager.setAutoUpdateCheck(true)
}
}
// Handle a start URI if set as start option.
@ -190,7 +192,7 @@ ApplicationWindow {
Connections {
target: {
if (Qt.platform.os !== "windows" && Qt.platform.os !== "macos")
if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
return DBusErrorHandler
return null
}
@ -220,7 +222,7 @@ ApplicationWindow {
JamiQmlUtils.mainApplicationScreen = root.screen
if (Qt.platform.os !== "windows" && Qt.platform.os !== "macos")
if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
DBusErrorHandler.setActive(true)
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (C) 2021-2022 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "updatemanager.h"
#ifdef ENABLE_SPARKLE
#include <Sparkle/Sparkle.h>
#endif
#ifdef BETA
static constexpr bool isBeta = true;
#else
static constexpr bool isBeta = false;
#endif
#ifdef ENABLE_SPARKLE
struct UpdateManager::Impl
{
Impl()
{
updaterController_ = [[SPUStandardUpdaterController alloc] initWithStartingUpdater: true
updaterDelegate: nil
userDriverDelegate: nil];
};
void checkForUpdates()
{
[updaterController_ checkForUpdates: nil];
};
void setAutoUpdateCheck(bool state)
{
updaterController_.updater.automaticallyChecksForUpdates = state;
};
bool isAutoUpdaterEnabled()
{
return updaterController_.updater.automaticallyChecksForUpdates;
};
bool isUpdaterEnabled() {
return true;
};
SPUStandardUpdaterController* updaterController_;
};
#else
struct UpdateManager::Impl
{
void checkForUpdates() {};
void setAutoUpdateCheck(bool state) {};
bool isAutoUpdaterEnabled()
{
return false;
};
bool isUpdaterEnabled()
{
return false;
};
};
#endif
UpdateManager::UpdateManager(const QString& url,
ConnectivityMonitor* cm,
LRCInstance* instance,
QObject* parent)
: NetWorkManager(cm, parent)
, pimpl_(std::make_unique<Impl>())
{}
UpdateManager::~UpdateManager()
{}
void
UpdateManager::checkForUpdates(bool quiet)
{
Q_UNUSED(quiet)
pimpl_->checkForUpdates();
}
void
UpdateManager::applyUpdates(bool beta)
{
Q_UNUSED(beta)
}
void
UpdateManager::cancelUpdate()
{}
void
UpdateManager::setAutoUpdateCheck(bool state)
{
pimpl_->setAutoUpdateCheck(state);
}
bool
UpdateManager::isCurrentVersionBeta()
{
return isBeta;
}
bool
UpdateManager::isUpdaterEnabled()
{
return pimpl_->isUpdaterEnabled();
}
bool
UpdateManager::isAutoUpdaterEnabled()
{
return pimpl_->isAutoUpdaterEnabled();
}

View file

@ -23,6 +23,7 @@ import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Enums 1.1
import net.jami.Constants 1.1
import net.jami.Helpers 1.1
import "../../commoncomponents"
@ -99,7 +100,7 @@ Rectangle {
Layout.leftMargin: JamiTheme.preferredMarginSize
Layout.rightMargin: JamiTheme.preferredMarginSize
Layout.bottomMargin: JamiTheme.preferredMarginSize
visible: Qt.platform.os == "windows" ? true : false
visible: UpdateManager.isUpdaterEnabled()
}
}
}

View file

@ -50,7 +50,9 @@ ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: JamiTheme.preferredMarginSize
checked: UtilsAdapter.getAppValue(Settings.Key.AutoUpdate)
checked: Qt.platform.os.toString() === "windows" ?
UtilsAdapter.getAppValue(Settings.Key.AutoUpdate) :
UpdateManager.isAutoUpdaterEnabled()
labelText: JamiStrings.update
tooltipText: JamiStrings.enableAutoUpdates
@ -84,7 +86,7 @@ ColumnLayout {
MaterialButton {
id: installBetaButton
visible: !UpdateManager.isCurrentVersionBeta()
visible: !UpdateManager.isCurrentVersionBeta() && Qt.platform.os.toString() === "windows"
Layout.alignment: Qt.AlignHCenter

View file

@ -1,4 +1,4 @@
/*!
/*
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
@ -38,31 +38,172 @@ static constexpr char betaVersionSubUrl[] = "/beta/version";
static constexpr char msiSubUrl[] = "/jami.release.x64.msi";
static constexpr char betaMsiSubUrl[] = "/beta/jami.beta.x64.msi";
struct UpdateManager::Impl : public QObject
{
Impl(const QString& url, ConnectivityMonitor* cm, LRCInstance* instance, UpdateManager& parent)
: QObject(nullptr)
, parent_(parent)
, lrcInstance_(instance)
, baseUrlString_(url.isEmpty() ? downloadUrl : url)
, tempPath_(Utils::WinGetEnv("TEMP"))
, updateTimer_(new QTimer(this))
{
connect(updateTimer_, &QTimer::timeout, [this] {
// Quiet period update check.
parent_.checkForUpdates(true);
});
};
~Impl() = default;
void checkForUpdates(bool quiet)
{
parent_.disconnect();
// Fail without UI if this is a programmatic check.
if (!quiet)
connect(&parent_,
&NetWorkManager::errorOccured,
&parent_,
&UpdateManager::updateCheckErrorOccurred);
cleanUpdateFiles();
QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
: QUrl::fromUserInput(baseUrlString_ + versionSubUrl)};
parent_.get(versionUrl, [this, quiet](const QString& latestVersionString) {
if (latestVersionString.isEmpty()) {
qWarning() << "Error checking version";
if (!quiet)
Q_EMIT parent_.updateCheckReplyReceived(false);
return;
}
auto currentVersion = QString(VERSION_STRING).toULongLong();
auto latestVersion = latestVersionString.toULongLong();
qDebug() << "latest: " << latestVersion << " current: " << currentVersion;
if (latestVersion > currentVersion) {
qDebug() << "New version found";
Q_EMIT parent_.updateCheckReplyReceived(true, true);
} else {
qDebug() << "No new version found";
if (!quiet)
Q_EMIT parent_.updateCheckReplyReceived(true, false);
}
});
};
void applyUpdates(bool beta = false)
{
parent_.disconnect();
connect(&parent_,
&NetWorkManager::errorOccured,
&parent_,
&UpdateManager::updateDownloadErrorOccurred);
connect(&parent_, &NetWorkManager::statusChanged, [this](GetStatus status) {
switch (status) {
case GetStatus::STARTED:
connect(&parent_,
&NetWorkManager::downloadProgressChanged,
&parent_,
&UpdateManager::updateDownloadProgressChanged);
Q_EMIT parent_.updateDownloadStarted();
break;
case GetStatus::FINISHED:
Q_EMIT parent_.updateDownloadFinished();
break;
default:
break;
}
});
QUrl downloadUrl {(beta || isBeta) ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
: QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
parent_.get(
downloadUrl,
[this, downloadUrl](const QString&) {
lrcInstance_->finish();
Q_EMIT lrcInstance_->quitEngineRequested();
auto args = QString(" /passive /norestart WIXNONUILAUNCH=1");
QProcess process;
process.start("powershell ",
QStringList() << tempPath_ + "\\" + downloadUrl.fileName() << "/L*V"
<< tempPath_ + "\\jami_x64_install.log" + args);
process.waitForFinished();
},
tempPath_);
};
void cancelUpdate()
{
parent_.cancelRequest();
};
void setAutoUpdateCheck(bool state)
{
// Quiet check for updates periodically, if set to.
if (!state) {
updateTimer_->stop();
return;
}
updateTimer_->start(updatePeriod);
};
void cleanUpdateFiles()
{
// Delete all logs and msi in the %TEMP% directory before launching.
QString dir = QString(Utils::WinGetEnv("TEMP"));
QDir log_dir(dir, {"jami*.log"});
for (const QString& filename : log_dir.entryList()) {
log_dir.remove(filename);
}
QDir msi_dir(dir, {"jami*.msi"});
for (const QString& filename : msi_dir.entryList()) {
msi_dir.remove(filename);
}
QDir version_dir(dir, {"version"});
for (const QString& filename : version_dir.entryList()) {
version_dir.remove(filename);
}
};
UpdateManager& parent_;
LRCInstance* lrcInstance_ {nullptr};
QString baseUrlString_;
QString tempPath_;
QTimer* updateTimer_;
};
UpdateManager::UpdateManager(const QString& url,
ConnectivityMonitor* cm,
LRCInstance* instance,
QObject* parent)
: NetWorkManager(cm, parent)
, lrcInstance_(instance)
, baseUrlString_(url.isEmpty() ? downloadUrl : url)
, tempPath_(Utils::WinGetEnv("TEMP"))
, updateTimer_(new QTimer(this))
, pimpl_(std::make_unique<Impl>(url, cm, instance, *this))
{}
UpdateManager::~UpdateManager() {}
void
UpdateManager::checkForUpdates(bool quiet)
{
connect(updateTimer_, &QTimer::timeout, [this] {
// Quiet period update check.
checkForUpdates(true);
});
pimpl_->checkForUpdates(quiet);
}
void
UpdateManager::applyUpdates(bool beta)
{
pimpl_->applyUpdates(beta);
}
void
UpdateManager::cancelUpdate()
{
pimpl_->cancelUpdate();
}
void
UpdateManager::setAutoUpdateCheck(bool state)
{
// Quiet check for updates periodically, if set to.
if (!state) {
updateTimer_->stop();
return;
}
updateTimer_->start(updatePeriod);
pimpl_->setAutoUpdateCheck(state);
}
bool
@ -71,102 +212,17 @@ UpdateManager::isCurrentVersionBeta()
return isBeta;
}
void
UpdateManager::checkForUpdates(bool quiet)
bool
UpdateManager::isUpdaterEnabled()
{
disconnect();
// Fail without UI if this is a programmatic check.
if (!quiet)
connect(this, &NetWorkManager::errorOccured, this, &UpdateManager::updateCheckErrorOccurred);
cleanUpdateFiles();
QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
: QUrl::fromUserInput(baseUrlString_ + versionSubUrl)};
get(versionUrl, [this, quiet](const QString& latestVersionString) {
if (latestVersionString.isEmpty()) {
qWarning() << "Error checking version";
if (!quiet)
Q_EMIT updateCheckReplyReceived(false);
return;
}
auto currentVersion = QString(VERSION_STRING).toULongLong();
auto latestVersion = latestVersionString.toULongLong();
qDebug() << "latest: " << latestVersion << " current: " << currentVersion;
if (latestVersion > currentVersion) {
qDebug() << "New version found";
Q_EMIT updateCheckReplyReceived(true, true);
} else {
qDebug() << "No new version found";
if (!quiet)
Q_EMIT updateCheckReplyReceived(true, false);
}
});
#ifdef Q_OS_WIN
return true;
#endif
return false;
}
void
UpdateManager::applyUpdates(bool beta)
bool
UpdateManager::isAutoUpdaterEnabled()
{
disconnect();
connect(this, &NetWorkManager::errorOccured, this, &UpdateManager::updateDownloadErrorOccurred);
connect(this, &NetWorkManager::statusChanged, [this](GetStatus status) {
switch (status) {
case GetStatus::STARTED:
connect(this,
&NetWorkManager::downloadProgressChanged,
this,
&UpdateManager::updateDownloadProgressChanged);
Q_EMIT updateDownloadStarted();
break;
case GetStatus::FINISHED:
Q_EMIT updateDownloadFinished();
break;
default:
break;
}
});
QUrl downloadUrl {(beta || isBeta) ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
: QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
get(
downloadUrl,
[this, downloadUrl](const QString&) {
lrcInstance_->finish();
Q_EMIT lrcInstance_->quitEngineRequested();
auto args = QString(" /passive /norestart WIXNONUILAUNCH=1");
QProcess process;
process.start("powershell ",
QStringList() << tempPath_ + "\\" + downloadUrl.fileName() << "/L*V"
<< tempPath_ + "\\jami_x64_install.log" + args);
process.waitForFinished();
},
tempPath_);
}
void
UpdateManager::cancelUpdate()
{
cancelRequest();
}
void
UpdateManager::cleanUpdateFiles()
{
/*
* Delete all logs and msi in the %TEMP% directory before launching.
*/
QString dir = QString(Utils::WinGetEnv("TEMP"));
QDir log_dir(dir, {"jami*.log"});
for (const QString& filename : log_dir.entryList()) {
log_dir.remove(filename);
}
QDir msi_dir(dir, {"jami*.msi"});
for (const QString& filename : msi_dir.entryList()) {
msi_dir.remove(filename);
}
QDir version_dir(dir, {"version"});
for (const QString& filename : version_dir.entryList()) {
version_dir.remove(filename);
}
return false;
}

View file

@ -1,4 +1,4 @@
/*!
/*
* Copyright (C) 2020-2022 Savoir-faire Linux Inc.
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
@ -20,25 +20,29 @@
#include "networkmanager.h"
#include <memory>
class LRCInstance;
class ConnectivityMonitor;
class QTimer;
class UpdateManager final : public NetWorkManager
{
Q_OBJECT
Q_DISABLE_COPY(UpdateManager)
public:
explicit UpdateManager(const QString& url,
ConnectivityMonitor* cm,
LRCInstance* instance = nullptr,
QObject* parent = nullptr);
~UpdateManager() = default;
~UpdateManager();
Q_INVOKABLE void checkForUpdates(bool quiet = false);
Q_INVOKABLE void applyUpdates(bool beta = false);
Q_INVOKABLE void cancelUpdate();
Q_INVOKABLE void setAutoUpdateCheck(bool state);
Q_INVOKABLE bool isCurrentVersionBeta();
Q_INVOKABLE bool isUpdaterEnabled();
Q_INVOKABLE bool isAutoUpdaterEnabled();
Q_SIGNALS:
void updateCheckReplyReceived(bool ok, bool found = false);
@ -50,13 +54,7 @@ Q_SIGNALS:
void appCloseRequested();
private:
// LRCInstance pointer
LRCInstance* lrcInstance_ {nullptr};
QString baseUrlString_;
QString tempPath_;
QTimer* updateTimer_;
void cleanUpdateFiles();
struct Impl;
std::unique_ptr<Impl> pimpl_;
};
Q_DECLARE_METATYPE(UpdateManager*)