Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Müller <fmueller@owncloud.com>2022-01-19 15:26:37 +0300
committerHannah von Reth <vonreth@kde.org>2022-03-14 16:23:05 +0300
commitd76f27f19ee91d4018dc2b407fb6516cc2d535f0 (patch)
tree364a9a39d54fb6e35bcdd39c848a7690109ea775 /src/gui/updater
parent71fe3f2e1e0f3c1948f8b72b31117cb90631f2db (diff)
Add built-in AppImage self updater
Performs upgrades in background if available. Uses libappimageupdate internally to update efficiently.
Diffstat (limited to 'src/gui/updater')
-rw-r--r--src/gui/updater/CMakeLists.txt28
-rw-r--r--src/gui/updater/appimageupdater.cpp165
-rw-r--r--src/gui/updater/appimageupdater.h41
-rw-r--r--src/gui/updater/ocupdater.h22
-rw-r--r--src/gui/updater/updater.cpp20
5 files changed, 264 insertions, 12 deletions
diff --git a/src/gui/updater/CMakeLists.txt b/src/gui/updater/CMakeLists.txt
index baf68498d..0f8021962 100644
--- a/src/gui/updater/CMakeLists.txt
+++ b/src/gui/updater/CMakeLists.txt
@@ -25,3 +25,31 @@ if(SPARKLE_FOUND)
)
target_link_libraries(owncloudCore PRIVATE ${SPARKLE_LIBRARY})
endif()
+
+if(WITH_APPIMAGEUPDATER)
+ # needed for set_source_files_properties(... TARGET_DIRECTORY ...)
+ cmake_minimum_required(VERSION 3.18)
+
+ message(STATUS "Including built-in libappimageupdate based updater")
+
+ set(appimageupdater_sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/appimageupdater.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/appimageupdater.h
+ )
+
+ target_sources(owncloudCore PRIVATE ${appimageupdater_sources})
+
+ # as libappimageupdate uses exceptions, we define a shim that catches all incoming exceptions
+ # and handles them in a suitable way
+ # we have to enable exceptions for this one compilation unit
+ set_source_files_properties(${appimageupdater_sources}
+ TARGET_DIRECTORY owncloudCore
+ PROPERTIES COMPILE_OPTIONS "-fexceptions"
+ )
+
+ target_compile_definitions(owncloudCore PRIVATE WITH_APPIMAGEUPDATER)
+ target_link_libraries(owncloudCore PRIVATE libappimageupdate-qt)
+
+ find_package(Threads REQUIRED)
+ target_link_libraries(owncloudCore PUBLIC ${CMAKE_THREAD_LIBS_INIT})
+endif()
diff --git a/src/gui/updater/appimageupdater.cpp b/src/gui/updater/appimageupdater.cpp
new file mode 100644
index 000000000..d28b99a38
--- /dev/null
+++ b/src/gui/updater/appimageupdater.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 by Fabian Müller <fmueller@owncloud.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 2 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.
+ */
+
+#include <QTimer>
+#include <appimage/update.h>
+#include <chrono>
+
+#include "appimageupdater.h"
+#include "common/version.h"
+
+using namespace OCC;
+using namespace std::chrono_literals;
+
+namespace {
+
+/**
+ * libappimageupdate uses exceptions, but the client does not
+ * This little shim adapts the interface to one usable within this project
+ */
+class AppImageUpdaterShim : public QObject
+{
+ Q_OBJECT
+
+private:
+ explicit AppImageUpdaterShim(const QString &zsyncFileUrl, QObject *parent = nullptr)
+ : QObject(parent)
+ , _updater(Utility::appImageLocation().toStdString(), true)
+ {
+ QString updateInformation(QStringLiteral("zsync|") + zsyncFileUrl);
+ _updater.setUpdateInformation(updateInformation.toStdString());
+ }
+
+ void _logStatusMessages()
+ {
+ std::string currentStatusMessage;
+
+ while (_updater.nextStatusMessage(currentStatusMessage)) {
+ qCInfo(lcUpdater) << "AppImageUpdate:" << QString::fromStdString(currentStatusMessage);
+ }
+ }
+
+public:
+ static AppImageUpdaterShim *makeInstance(const QString &updateInformation, QObject *parent)
+ {
+ try {
+ return new AppImageUpdaterShim(updateInformation, parent);
+ } catch (const std::exception &e) {
+ qCCritical(lcUpdater) << "Failed to create updater shim:" << e.what();
+ return nullptr;
+ }
+ }
+
+ bool isUpdateAvailable() noexcept
+ {
+ try {
+ bool updateAvailable;
+
+ if (!_updater.checkForChanges(updateAvailable)) {
+ _logStatusMessages();
+ return false;
+ }
+
+ _logStatusMessages();
+ return updateAvailable;
+ } catch (const std::exception &e) {
+ _logStatusMessages();
+ qCCritical(lcUpdater) << "Checking for update failed:" << e.what();
+ return false;
+ }
+ }
+
+ void startUpdateInBackground() noexcept
+ {
+ // monitor progress and log status messages
+ auto *timer = new QTimer(this);
+
+ timer->setInterval(100ms);
+
+ connect(timer, &QTimer::timeout, this, [=]() {
+ _logStatusMessages();
+
+ if (_updater.isDone()) {
+ emit finished(!_updater.hasError());
+ timer->stop();
+ }
+ });
+
+ _updater.start();
+ timer->start();
+ }
+
+signals:
+ void finished(bool successfully);
+
+private:
+ appimage::update::Updater _updater;
+};
+
+} // namespace
+
+AppImageUpdater::AppImageUpdater(const QUrl &url)
+ : OCUpdater(url)
+{
+}
+
+
+bool AppImageUpdater::handleStartup()
+{
+ // nothing to do, update will be performed while app is running, if anything
+ return false;
+}
+
+void AppImageUpdater::versionInfoArrived(const UpdateInfo &info)
+{
+ if (info.version().isEmpty() || Version::versionWithBuildNumber() >= QVersionNumber::fromString(info.version())) {
+ qCInfo(lcUpdater) << "Client is on latest version!";
+ setDownloadState(UpToDate);
+ return;
+ }
+
+ const auto AppImageUpdaterShim = AppImageUpdaterShim::makeInstance(info.downloadUrl(), this);
+
+ if (AppImageUpdaterShim == nullptr) {
+ setDownloadState(DownloadFailed);
+ return;
+ }
+
+ if (!AppImageUpdaterShim->isUpdateAvailable()) {
+ qCCritical(lcUpdater) << "Update server reported that update is available, but AppImageUpdate disagrees, aborting";
+ setDownloadState(DownloadFailed);
+ return;
+ }
+
+ // binding AppImageUpdaterShim shared pointer to finished callback makes sure the updater is cleaned up when it's done
+ connect(AppImageUpdaterShim, &AppImageUpdaterShim::finished, this, [this](bool succeeded) {
+ if (succeeded) {
+ qCInfo(lcUpdater) << "AppImage update complete";
+ setDownloadState(DownloadComplete);
+ } else {
+ qCInfo(lcUpdater) << "AppImage update failed";
+ setDownloadState(DownloadFailed);
+ }
+ });
+
+ setDownloadState(Downloading);
+ AppImageUpdaterShim->startUpdateInBackground();
+}
+
+void AppImageUpdater::backgroundCheckForUpdate()
+{
+ OCUpdater::backgroundCheckForUpdate();
+}
+
+#include "appimageupdater.moc"
diff --git a/src/gui/updater/appimageupdater.h b/src/gui/updater/appimageupdater.h
new file mode 100644
index 000000000..183126db1
--- /dev/null
+++ b/src/gui/updater/appimageupdater.h
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2022 by Fabian Müller <fmueller@owncloud.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 2 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.
+*/
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+#include <updater/ocupdater.h>
+
+namespace OCC {
+
+/**
+ * @brief AppImage Updater using AppImageUpdate
+ * @ingroup gui
+ */
+class AppImageUpdater : public OCUpdater
+{
+ Q_OBJECT
+
+public:
+ explicit AppImageUpdater(const QUrl &url);
+ bool handleStartup() override;
+ void backgroundCheckForUpdate() override;
+
+private:
+ void versionInfoArrived(const UpdateInfo &succeeded) override;
+};
+
+} // namespace OCC
diff --git a/src/gui/updater/ocupdater.h b/src/gui/updater/ocupdater.h
index dbdb6be51..791ab0660 100644
--- a/src/gui/updater/ocupdater.h
+++ b/src/gui/updater/ocupdater.h
@@ -49,17 +49,17 @@ namespace OCC {
* Simple class diagram of the updater:
*
* +---------------------------+
- * +-----+ UpdaterScheduler +-----+
- * | +------------+--------------+ |
- * v v v
- * +------------+ +---------------------+ +----------------+
- * |NSISUpdater | |PassiveUpdateNotifier| | SparkleUpdater |
- * +-+----------+ +---+-----------------+ +-----+----------+
- * | | |
- * | v +------------------+
- * | +---------------+ v
- * +-->| OCUpdater +------+
- * +--------+------+ |
+ * +-----+ UpdaterScheduler +-----+------------------+
+ * | +------------+--------------+ | |
+ * v v v v
+ * +------------+ +---------------------+ +----------------+ +-----------------+
+ * |NSISUpdater | |PassiveUpdateNotifier| | SparkleUpdater | | AppImageUpdater |
+ * +-+----------+ +---+-----------------+ +-----+----------+ +-----------------+
+ * | | | |
+ * | v +------------------+ |
+ * | +---------------+ v |
+ * +-->| OCUpdater +------+ |
+ * +--------+------+ |<--------------------------------+
* | Updater |
* +-------------+
*/
diff --git a/src/gui/updater/updater.cpp b/src/gui/updater/updater.cpp
index 8713989e9..8b8a6d854 100644
--- a/src/gui/updater/updater.cpp
+++ b/src/gui/updater/updater.cpp
@@ -20,6 +20,10 @@
#include "updater/sparkleupdater.h"
#include "updater/ocupdater.h"
+#ifdef WITH_APPIMAGEUPDATER
+#include "updater/appimageupdater.h"
+#endif
+
#include "common/utility.h"
#include "common/version.h"
#include "configfile.h"
@@ -72,7 +76,15 @@ QUrlQuery Updater::getQueryParams()
Theme *theme = Theme::instance();
QString platform = QStringLiteral("stranger");
if (Utility::isLinux()) {
- platform = QStringLiteral("linux");
+#ifdef WITH_APPIMAGEUPDATER
+ if (Utility::runningInAppImage()) {
+ platform = "linux-appimage-" + QSysInfo::buildCpuArchitecture();
+ } else {
+#endif
+ platform = QStringLiteral("linux");
+#ifdef WITH_APPIMAGEUPDATER
+ }
+#endif
} else if (Utility::isBSD()) {
platform = QStringLiteral("bsd");
} else if (Utility::isWindows()) {
@@ -135,6 +147,12 @@ Updater *Updater::create()
// Also for MSI
return new NSISUpdater(url);
#else
+#ifdef WITH_APPIMAGEUPDATER
+ if (Utility::runningInAppImage()) {
+ return new AppImageUpdater(url);
+ }
+#endif
+
// the best we can do is notify about updates
return new PassiveUpdateNotifier(url);
#endif