From f0b55e68e3e3998a0a1b4434108777aa7f29281f Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Thu, 21 Aug 2025 13:14:53 +0100 Subject: [PATCH 1/2] UpdateHandler: Check against releases not commits --- YUViewLib/src/handler/UpdateHandler.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/YUViewLib/src/handler/UpdateHandler.cpp b/YUViewLib/src/handler/UpdateHandler.cpp index ff0b31690..5056d8f8e 100644 --- a/YUViewLib/src/handler/UpdateHandler.cpp +++ b/YUViewLib/src/handler/UpdateHandler.cpp @@ -157,11 +157,11 @@ void updateHandler::startCheckForNewVersion(bool userRequest, bool force) } else if (VERSION_CHECK) { - // We can check the Github API for the commit hash. After that we can say if there is a new version available on Github. + // We can check the Github API for the release. After that we can say if there is a new version available on Github. updaterStatus = updaterChecking; userCheckRequest = userRequest; - DEBUG_UPDATE("updateHandler::startCheckForNewVersion get https://api.github.com/repos/IENT/YUView/commits"); - networkManager.get(QNetworkRequest(QUrl("https://api.github.com/repos/IENT/YUView/commits"))); + DEBUG_UPDATE("updateHandler::startCheckForNewVersion get https://api.github.com/repos/IENT/YUView/releases"); + networkManager.get(QNetworkRequest(QUrl("https://api.github.com/repos/IENT/YUView/releases"))); } else { @@ -278,16 +278,16 @@ void updateHandler::replyFinished(QNetworkReply *reply) else { QJsonObject jsonObject = jsonArray[0].toObject(); - if (!jsonObject.contains("sha")) + if (!jsonObject.contains("tag_name")) { error = true; - errorString = "The returned JSON object does not contain sha information on the latest commit hash."; + errorString = "The returned JSON object does not contain version information on the latest release."; } else { - QString serverHash = jsonObject["sha"].toString(); - QString buildHash = QString::fromUtf8(YUVIEW_HASH); - if (serverHash != buildHash) + QString serverVersion = jsonObject["tag_name"].toString(); + QString buildVersion = QString::fromUtf8(YUVIEW_VERSION); + if (serverVersion != buildVersion) { QMessageBox msgBox; msgBox.setTextFormat(Qt::RichText); From 23b920c76dd3a70844789a3c3bc8b2517c2191a0 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 10 Aug 2026 23:15:53 +0200 Subject: [PATCH 2/2] Move out things a bit so that we can add more components and tests. --- YUViewLib/src/handler/UpdateHandler.h | 143 ------- YUViewLib/src/handler/update/UpdateDialog.cpp | 78 ++++ YUViewLib/src/handler/update/UpdateDialog.h | 50 +++ .../UpdateFileHandler.cpp} | 32 +- .../UpdateFileHandler.h} | 10 +- .../handler/{ => update}/UpdateHandler.cpp | 400 +++++++++--------- YUViewLib/src/handler/update/UpdateHandler.h | 131 ++++++ YUViewLib/src/ui/Mainwindow.cpp | 2 +- YUViewLib/src/ui/Mainwindow.h | 4 +- .../src/ui/widgets/PlaylistTreeWidget.cpp | 8 +- 10 files changed, 498 insertions(+), 360 deletions(-) delete mode 100644 YUViewLib/src/handler/UpdateHandler.h create mode 100644 YUViewLib/src/handler/update/UpdateDialog.cpp create mode 100644 YUViewLib/src/handler/update/UpdateDialog.h rename YUViewLib/src/handler/{UpdateHandlerFile.cpp => update/UpdateFileHandler.cpp} (84%) rename YUViewLib/src/handler/{UpdateHandlerFile.h => update/UpdateFileHandler.h} (93%) rename YUViewLib/src/handler/{ => update}/UpdateHandler.cpp (52%) create mode 100644 YUViewLib/src/handler/update/UpdateHandler.h diff --git a/YUViewLib/src/handler/UpdateHandler.h b/YUViewLib/src/handler/UpdateHandler.h deleted file mode 100644 index 18e9c9fc3..000000000 --- a/YUViewLib/src/handler/UpdateHandler.h +++ /dev/null @@ -1,143 +0,0 @@ -/* This file is part of YUView - The YUV player with advanced analytics toolset -* -* Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY -* -* 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. -* -* In addition, as a special exception, the copyright holders give -* permission to link the code of portions of this program with the -* OpenSSL library under certain conditions as described in each -* individual source file, and distribute linked combinations including -* the two. -* -* You must obey the GNU General Public License in all respects for all -* of the code used other than OpenSSL. If you modify file(s) with this -* exception, you may extend this exception to your version of the -* file(s), but you are not obligated to do so. If you do not wish to do -* so, delete this exception statement from your version. If you delete -* this exception statement from all source files in the program, then -* also delete it here. -* -* 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 . -*/ - -#pragma once - -#include - -#include -#include - -#include "ui_updateDialog.h" - -class QNetworkReply; -class QProgressDialog; - -// Ask the user if he wants to update to the new version and how to handle updates in the future. -class UpdateDialog : public QDialog -{ - Q_OBJECT - -public: - explicit UpdateDialog(QWidget *parent = 0); - -private slots: - void on_updateButton_clicked(); - -private: - Ui::UpdateDialog ui; -}; - -/* The update handler does what it's name suggestes. It handles updates for YUView. - * Updates are enabled if UPDATE_FEATURE_ENABLE is set to 1. In order for automatic - * updates to work, different compilations of YUView must not be mixed. Therefor, - * the UPDATE_FEATURE_ENABLE flag is set by out buildbot before compilation. The resulting - * binary files are then put on github so that the updater can donwload them from there. - * - * The first step is to establish a list of files that we need to download/update. For this, - * we download the file 'versioninfo.txt' from github. We then compare that to the local - * 'versioninfo.txt' file to get a list of files that need to be downloaded. - * - * On windows, we need administrative rights to write to the 'Program Files' folder or even - * to rename files. So first, we restart YUView with elevated rights and the command line - * argument 'updateElevated'. This argument will let YUView know, to immediately perform the - * update without asking the user again. - * - * The update process itself then works like this: We remove the files that need updating - * and download the new versions. If a file can not be removed, we rename it to "Something_old.ext". - * If the _old file already exists, it is left from a previous update and we should be able - * to delete it now. When everything is done, we restart YUView one final time to start the - * now updated version of YUView. -*/ -class updateHandler : public QObject -{ - Q_OBJECT - -public: - // Construct a new update handler. The mainWindows pointer is used if a dialog is shown. - updateHandler(QWidget *mainWindow, bool useAlternativeSources); - -public slots: - // Send the request to check for a new version of YUView - void startCheckForNewVersion(bool userRequest=true, bool force=false); - - // The windows process should have elevated rights now and we can do the update - void forceUpdateElevated(); - -private slots: - void replyFinished(QNetworkReply *reply); - void downloadFinished(QNetworkReply *reply); - void updateDownloadProgress(int64_t val, int64_t max); - void sslErrors(QNetworkReply * reply, const QList & errors); - -private: - void downloadAndInstallUpdate(); - void restartYUView(bool elevated); - - // Abort the update (reset updaterStatus to idle and show a QMessageBox::critical with the given message) - void abortUpdate(QString errorMsg); - - QPointer mainWidget; - QNetworkAccessManager networkManager; - - QPointer downloadProgress; - - enum updaterStatusEnum - { - updaterIdle, // The updater is idle. We can start checking for an update. - updaterEstablishConnection, // The updater is trying to establish a secure connection - updaterChecking, // The updater is currently checking for an update. Don't start another check. - updaterDownloading // The updater is currently donwloading/installing updates. Do not start another check for updates. - }; - updaterStatusEnum updaterStatus { updaterIdle }; - - bool userCheckRequest { false }; //< The request has been issued by the user. - bool elevatedRights { false }; // On windows this can indicate if the process should have elevated rights - bool forceUpdate { false }; // If an update is availabe and this is set, we will just install the update no matter what - bool useAlternativeSources { false }; // Use the alternative (test) source to get the update files - - // The list or remote files we are downloading. For each file, we keep the path and name and it's size in bytes. - QList> downloadFiles; - - // Initiate the download of the next file. - void downloadNextFile(); - // The full name (including subdirs) and size of the file being downloaded currently - QPair currentDownloadFile; - - // When downloading files is started, these contains the size (in bytes) of all files to be downloaded and the - // current amount of bytes that were already downloaded. - int totalDownloadSize; - int currentDownloadProgress; - - QString updatePath{}; -}; - diff --git a/YUViewLib/src/handler/update/UpdateDialog.cpp b/YUViewLib/src/handler/update/UpdateDialog.cpp new file mode 100644 index 000000000..df33ebe87 --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateDialog.cpp @@ -0,0 +1,78 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * 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. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * 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 . + */ + +#include "UpdateDialog.h" + +#include + +#include + +UpdateDialog::UpdateDialog(QWidget *parent) : QDialog(parent) +{ + ui.setupUi(this); + + // Load the update settings from the QSettings + QSettings settings; + settings.beginGroup("updates"); + bool checkForUpdates = settings.value("checkForUpdates", true).toBool(); + QString updateBehavior = settings.value("updateBehavior", "ask").toString(); + settings.endGroup(); + + ui.checkUpdatesGroupBox->setChecked(checkForUpdates); + if (updateBehavior == "ask") + ui.updateSettingComboBox->setCurrentIndex(1); + else if (updateBehavior == "auto") + ui.updateSettingComboBox->setCurrentIndex(0); + + connect(ui.cancelButton, &QPushButton::clicked, this, &QDialog::reject); + + if (!UPDATE_FEATURE_ENABLE) + // If the update feature is not available, we will grey this out. + ui.updateSettingComboBox->setEnabled(false); +} + +void UpdateDialog::on_updateButton_clicked() +{ + // The user wants to download/install the update. + + // First save the settings + QSettings settings; + settings.beginGroup("updates"); + settings.setValue("checkForUpdates", ui.checkUpdatesGroupBox->isChecked()); + QString updateBehavior = "ask"; + if (ui.updateSettingComboBox->currentIndex() == 0) + updateBehavior = "auto"; + settings.setValue("updateBehavior", updateBehavior); + + // The update request was accepted by the user + accept(); +} \ No newline at end of file diff --git a/YUViewLib/src/handler/update/UpdateDialog.h b/YUViewLib/src/handler/update/UpdateDialog.h new file mode 100644 index 000000000..7d95d564d --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateDialog.h @@ -0,0 +1,50 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * 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. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * 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 . + */ + +#pragma once + +#include "ui_updateDialog.h" + +// Ask the user if he wants to update to the new version and how to handle updates in the future. +class UpdateDialog : public QDialog +{ + Q_OBJECT + +public: + explicit UpdateDialog(QWidget *parent = 0); + +private slots: + void on_updateButton_clicked(); + +private: + Ui::UpdateDialog ui; +}; diff --git a/YUViewLib/src/handler/UpdateHandlerFile.cpp b/YUViewLib/src/handler/update/UpdateFileHandler.cpp similarity index 84% rename from YUViewLib/src/handler/UpdateHandlerFile.cpp rename to YUViewLib/src/handler/update/UpdateFileHandler.cpp index 034e849c7..0cce929f6 100644 --- a/YUViewLib/src/handler/UpdateHandlerFile.cpp +++ b/YUViewLib/src/handler/update/UpdateFileHandler.cpp @@ -30,7 +30,7 @@ * along with this program. If not, see . */ -#include "UpdateHandlerFile.h" +#include "UpdateFileHandler.h" #include #include @@ -43,31 +43,31 @@ #define DEBUG_UPDATE_FILE(msg) ((void)0) #endif -const auto UPDATEFILEHANDLER_FILE_NAME = "versioninfo.txt"; +const auto UpdateFileHandler_FILE_NAME = "versioninfo.txt"; -updateFileHandler::updateFileHandler() +UpdateFileHandler::UpdateFileHandler() {} -updateFileHandler::updateFileHandler(QString fileName, QString updatePath) : +UpdateFileHandler::UpdateFileHandler(QString fileName, QString updatePath) : updatePath(updatePath) { this->readFromFile(fileName); } -updateFileHandler::updateFileHandler(QByteArray &byteArray) +UpdateFileHandler::UpdateFileHandler(QByteArray &byteArray) { this->readRemoteFromData(byteArray); } -void updateFileHandler::readFromFile(QString fileName) +void UpdateFileHandler::readFromFile(QString fileName) { - DEBUG_UPDATE_FILE("updateFileHandler::readFromFile Current working dir " << this->updatePath); + DEBUG_UPDATE_FILE("UpdateFileHandler::readFromFile Current working dir " << this->updatePath); // Open the file and get all files and their current version (int) from the file. QFileInfo updateFileInfo(fileName); if (!updateFileInfo.exists() || !updateFileInfo.isFile()) { - DEBUG_UPDATE_FILE("updateFileHandler::readFromFile local update file " << fileName << " not found"); + DEBUG_UPDATE_FILE("UpdateFileHandler::readFromFile local update file " << fileName << " not found"); return; } @@ -87,7 +87,7 @@ void updateFileHandler::readFromFile(QString fileName) this->loaded = true; } -void updateFileHandler::readRemoteFromData(QByteArray &arr) +void UpdateFileHandler::readRemoteFromData(QByteArray &arr) { const QString reply = QString(arr); const QStringList lines = reply.split("\n"); @@ -95,13 +95,13 @@ void updateFileHandler::readRemoteFromData(QByteArray &arr) this->parseOneLine(line); } -void updateFileHandler::parseOneLine(QString &line, bool checkExistence) +void UpdateFileHandler::parseOneLine(QString &line, bool checkExistence) { QStringList lineSplit = line.split(" "); if (line.startsWith("Last Commit")) { if (line.startsWith("Last Commit: ")) - DEBUG_UPDATE_FILE("updateFileHandler::parseOneLine Local file last commit: " << lineSplit[2]); + DEBUG_UPDATE_FILE("UpdateFileHandler::parseOneLine Local file last commit: " << lineSplit[2]); return; } // Ignore all lines that start with %, / or # @@ -120,7 +120,7 @@ void updateFileHandler::parseOneLine(QString &line, bool checkExistence) else // The file does not exist locally. That is strange since it is in the update info file. // Files that do not exist locally should always be downloaded so we don't put them into the list. - DEBUG_UPDATE_FILE("updateFileHandler::parseOneLine The local file " << fInfo.absoluteFilePath() << " could not be found."); + DEBUG_UPDATE_FILE("UpdateFileHandler::parseOneLine The local file " << fInfo.absoluteFilePath() << " could not be found."); } else // Do not check if the file exists @@ -128,7 +128,7 @@ void updateFileHandler::parseOneLine(QString &line, bool checkExistence) } } -QList updateFileHandler::getFilesToUpdate(updateFileHandler &localFiles) const +QList UpdateFileHandler::getFilesToUpdate(UpdateFileHandler &localFiles) const { QList updateList; for (auto remoteFile : this->updateFileList) @@ -149,11 +149,11 @@ QList updateFileHandler::getFilesToUpdate(updateFileHandler &local updateList.append(downloadFile(remoteFile.filePath, remoteFile.fileSize)); } // No matter what, we will update the "versioninfo.txt" file (assume it to be 10kbyte) - updateList.append(downloadFile(UPDATEFILEHANDLER_FILE_NAME, 10000)); + updateList.append(downloadFile(UpdateFileHandler_FILE_NAME, 10000)); return updateList; } -QString updateFileHandler::getInfo() const +QString UpdateFileHandler::getInfo() const { QString s; for (auto f : this->updateFileList) @@ -163,7 +163,7 @@ QString updateFileHandler::getInfo() const return s; } -updateFileHandler::fileListEntry updateFileHandler::createFileEntry(QStringList &lineSplit) const +UpdateFileHandler::fileListEntry UpdateFileHandler::createFileEntry(QStringList &lineSplit) const { fileListEntry entry; diff --git a/YUViewLib/src/handler/UpdateHandlerFile.h b/YUViewLib/src/handler/update/UpdateFileHandler.h similarity index 93% rename from YUViewLib/src/handler/UpdateHandlerFile.h rename to YUViewLib/src/handler/update/UpdateFileHandler.h index 1933efb6f..5ae1aecc8 100644 --- a/YUViewLib/src/handler/UpdateHandlerFile.h +++ b/YUViewLib/src/handler/update/UpdateFileHandler.h @@ -39,12 +39,12 @@ typedef QPair downloadFile; -class updateFileHandler +class UpdateFileHandler { public: - updateFileHandler(); - updateFileHandler(QString fileName, QString updatePath); - updateFileHandler(QByteArray &byteArray); + UpdateFileHandler(); + UpdateFileHandler(QString fileName, QString updatePath); + UpdateFileHandler(QByteArray &byteArray); // Parse the local file list and add all files that exist locally to the list of files // which potentially might require an update. @@ -60,7 +60,7 @@ class updateFileHandler // Call this on the remote file list with a reference to the local file list to get a list // of files that require an update (that need to be downloaded). - QList getFilesToUpdate(updateFileHandler &localFiles) const; + QList getFilesToUpdate(UpdateFileHandler &localFiles) const; QString getInfo() const; diff --git a/YUViewLib/src/handler/UpdateHandler.cpp b/YUViewLib/src/handler/update/UpdateHandler.cpp similarity index 52% rename from YUViewLib/src/handler/UpdateHandler.cpp rename to YUViewLib/src/handler/update/UpdateHandler.cpp index 5056d8f8e..53737e1b9 100644 --- a/YUViewLib/src/handler/UpdateHandler.cpp +++ b/YUViewLib/src/handler/update/UpdateHandler.cpp @@ -1,38 +1,39 @@ /* This file is part of YUView - The YUV player with advanced analytics toolset -* -* Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY -* -* 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. -* -* In addition, as a special exception, the copyright holders give -* permission to link the code of portions of this program with the -* OpenSSL library under certain conditions as described in each -* individual source file, and distribute linked combinations including -* the two. -* -* You must obey the GNU General Public License in all respects for all -* of the code used other than OpenSSL. If you modify file(s) with this -* exception, you may extend this exception to your version of the -* file(s), but you are not obligated to do so. If you do not wish to do -* so, delete this exception statement from your version. If you delete -* this exception statement from all source files in the program, then -* also delete it here. -* -* 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 . -*/ + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * 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. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * 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 . + */ #include "UpdateHandler.h" -#include "UpdateHandlerFile.h" +#include "UpdateDialog.h" +#include "UpdateFileHandler.h" #include #include @@ -42,8 +43,8 @@ #include #include #include -#include #include +#include #include #include @@ -57,7 +58,7 @@ // ONLY USE THIS FOR DEBGGING #define ALLOW_UNENCRYPTED_CONNECTIONS 0 -#define UPDATER_DEBUG_OUTPUT 0 +#define UPDATER_DEBUG_OUTPUT 1 #if UPDATER_DEBUG_OUTPUT && !NDEBUG #include #define DEBUG_UPDATE(msg) qDebug() << msg @@ -67,48 +68,54 @@ #define UPDATEFILEHANDLER_FILE_NAME "versioninfo.txt" #if ALLOW_UNENCRYPTED_CONNECTIONS -#define UPDATEFILEHANDLER_URL "https://raw.githubusercontent.com/IENT/YUViewReleases/master/win/autoupdate/" -#define UPDATEFILEHANDLER_TESTDEPLOY_URL "https://raw.githubusercontent.com/IENT/YUViewReleases/dev/win/autoupdate/" +#define UPDATEFILEHANDLER_URL \ + "https://raw.githubusercontent.com/IENT/YUViewReleases/master/win/autoupdate/" +#define UPDATEFILEHANDLER_TESTDEPLOY_URL \ + "https://raw.githubusercontent.com/IENT/YUViewReleases/dev/win/autoupdate/" #else -#define UPDATEFILEHANDLER_URL "https://raw.githubusercontent.com/IENT/YUViewReleases/master/win/autoupdate/" -#define UPDATEFILEHANDLER_TESTDEPLOY_URL "https://raw.githubusercontent.com/IENT/YUViewReleases/dev/win/autoupdate/" +#define UPDATEFILEHANDLER_URL \ + "https://raw.githubusercontent.com/IENT/YUViewReleases/master/win/autoupdate/" +#define UPDATEFILEHANDLER_TESTDEPLOY_URL \ + "https://raw.githubusercontent.com/IENT/YUViewReleases/dev/win/autoupdate/" #endif -updateHandler::updateHandler(QWidget *mainWindow, bool useAltSources) : - mainWidget(mainWindow) +UpdateHandler::UpdateHandler(QWidget *mainWindow, bool useAltSources) : mainWidget(mainWindow) { // We always perform the update in the path that the current executable is located in // and not in the current working directory. QFileInfo info(QCoreApplication::applicationFilePath()); - this->updatePath = info.absolutePath() + "/"; + this->updatePath = info.absolutePath() + "/"; useAlternativeSources = useAltSources; - connect(&networkManager, &QNetworkAccessManager::finished, this, &updateHandler::replyFinished); - connect(&networkManager, &QNetworkAccessManager::sslErrors, this, &updateHandler::sslErrors); + connect(&networkManager, &QNetworkAccessManager::finished, this, &UpdateHandler::replyFinished); + connect(&networkManager, &QNetworkAccessManager::sslErrors, this, &UpdateHandler::sslErrors); } -void updateHandler::sslErrors(QNetworkReply *reply, const QList &errors) +void UpdateHandler::sslErrors(QNetworkReply *, const QList &errors) { - QMessageBox::information(mainWidget, "SSL Connection error", "An error occurred while trying to establish a secure coonection to the server raw.githubusercontent.com."); - + QMessageBox::information(mainWidget, + "SSL Connection error", + "An error occurred while trying to establish a secure coonection to the " + "server raw.githubusercontent.com."); + // Abort - forceUpdate = false; + forceUpdate = false; userCheckRequest = false; - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; #if UPDATER_DEBUG_OUTPUT && !NDEBUG - DEBUG_UPDATE("updateHandler::sslErrors"); + DEBUG_UPDATE("UpdateHandler::sslErrors"); for (auto s : errors) { QString errorString = s.errorString(); qDebug() << s.errorString(); - auto cert = s.certificate(); + auto cert = s.certificate(); QStringList certText = cert.toText().split("\n"); for (QString s : certText) qDebug() << s; - auto altNames = cert.subjectAlternativeNames(); + auto altNames = cert.subjectAlternativeNames(); QMultiMap::iterator i = altNames.begin(); while (i != altNames.end()) { @@ -117,99 +124,113 @@ void updateHandler::sslErrors(QNetworkReply *reply, const QList &erro } } #else - (void)reply; (void)errors; #endif } // Start the asynchronous checking for an update. -void updateHandler::startCheckForNewVersion(bool userRequest, bool force) +void UpdateHandler::startCheckForNewVersion(bool userRequest, bool force) { QSettings settings; settings.beginGroup("updates"); bool checkForUpdates = settings.value("checkForUpdates", true).toBool(); - forceUpdate = force; + forceUpdate = force; settings.endGroup(); if (!userRequest && !checkForUpdates && !forceUpdate) - // The user did not request this, we are not automatocally checking for updates and it is not a forced check. Abort. + // The user did not request this, we are not automatocally checking for updates and it is not a + // forced check. Abort. return; - if (updaterStatus != updaterIdle) + if (updaterStatus != UpdateStatus::Idle) // The updater is busy. Do not start another check for updates. return; if (UPDATE_FEATURE_ENABLE && is_Q_OS_WIN) { // We are on windows and the update feature is available. - // Check the Github repository branch binariesAutoUpdate if there is a new version of the YUView executable available. - // First we will try to establish a secure connection to raw.githubusercontent.com + // Check the Github repository branch binariesAutoUpdate if there is a new version of the YUView + // executable available. First we will try to establish a secure connection to + // raw.githubusercontent.com #if ALLOW_UNENCRYPTED_CONNECTIONS - DEBUG_UPDATE("updateHandler::startCheckForNewVersion connectToHost raw.githubusercontent.com"); - updaterStatus = updaterEstablishConnection; + DEBUG_UPDATE("UpdateHandler::startCheckForNewVersion connectToHost raw.githubusercontent.com"); + updaterStatus = UpdateStatus::EstablishConnection; userCheckRequest = userRequest; networkManager.connectToHost("raw.githubusercontent.com"); #else - DEBUG_UPDATE("updateHandler::startCheckForNewVersion connectToHostEncrypted raw.githubusercontent.com"); - updaterStatus = updaterEstablishConnection; + DEBUG_UPDATE( + "UpdateHandler::startCheckForNewVersion connectToHostEncrypted raw.githubusercontent.com"); + updaterStatus = UpdateStatus::EstablishConnection; userCheckRequest = userRequest; networkManager.connectToHostEncrypted("raw.githubusercontent.com"); #endif } else if (VERSION_CHECK) { - // We can check the Github API for the release. After that we can say if there is a new version available on Github. - updaterStatus = updaterChecking; + // We can check the Github API for the release. After that we can say if there is a new version + // available on Github. + updaterStatus = UpdateStatus::Checking; userCheckRequest = userRequest; - DEBUG_UPDATE("updateHandler::startCheckForNewVersion get https://api.github.com/repos/IENT/YUView/releases"); + DEBUG_UPDATE("UpdateHandler::startCheckForNewVersion get " + "https://api.github.com/repos/IENT/YUView/releases"); networkManager.get(QNetworkRequest(QUrl("https://api.github.com/repos/IENT/YUView/releases"))); } else { // We don't know the current version. We cannot check if there is a newer one. if (userRequest) - QMessageBox::information(mainWidget, "Can not check for updates", "Unfortunately no version information has been compiled into this YUView version. Because of this we cannot check for updates."); + QMessageBox::information(mainWidget, + "Can not check for updates", + "Unfortunately no version information has been compiled into this " + "YUView version. Because of this we cannot check for updates."); } } // There is an answer from the server. -void updateHandler::replyFinished(QNetworkReply *reply) +void UpdateHandler::replyFinished(QNetworkReply *reply) { - if (updaterStatus == updaterDownloading) + if (updaterStatus == UpdateStatus::Downloading) { downloadFinished(reply); return; } - bool error = (reply->error() != QNetworkReply::NoError); + bool error = (reply->error() != QNetworkReply::NoError); QString errorString; if (error) errorString = reply->errorString(); - DEBUG_UPDATE("updateHandler::replyFinished " << (error ? "error " : "") << reply->error()); + DEBUG_UPDATE("UpdateHandler::replyFinished " << (error ? "error " : "") << reply->error()); if (UPDATE_FEATURE_ENABLE && is_Q_OS_WIN) { - if (updaterStatus == updaterEstablishConnection && !error) + if (updaterStatus == UpdateStatus::EstablishConnection && !error) { // The secure connection was successfully established. Now request the update.txt file if (useAlternativeSources) { - DEBUG_UPDATE("updateHandler::replyFinished request version info file from" UPDATEFILEHANDLER_TESTDEPLOY_URL UPDATEFILEHANDLER_FILE_NAME); - networkManager.get(QNetworkRequest(QUrl(UPDATEFILEHANDLER_TESTDEPLOY_URL UPDATEFILEHANDLER_FILE_NAME))); + DEBUG_UPDATE("UpdateHandler::replyFinished request version info file " + "from" UPDATEFILEHANDLER_TESTDEPLOY_URL UPDATEFILEHANDLER_FILE_NAME); + networkManager.get( + QNetworkRequest(QUrl(UPDATEFILEHANDLER_TESTDEPLOY_URL UPDATEFILEHANDLER_FILE_NAME))); } else { - DEBUG_UPDATE("updateHandler::replyFinished request version info file from" UPDATEFILEHANDLER_URL UPDATEFILEHANDLER_FILE_NAME); - networkManager.get(QNetworkRequest(QUrl(UPDATEFILEHANDLER_URL UPDATEFILEHANDLER_FILE_NAME))); + DEBUG_UPDATE( + "UpdateHandler::replyFinished request version info file from" UPDATEFILEHANDLER_URL + UPDATEFILEHANDLER_FILE_NAME); + networkManager.get( + QNetworkRequest(QUrl(UPDATEFILEHANDLER_URL UPDATEFILEHANDLER_FILE_NAME))); } - updaterStatus = updaterChecking; + updaterStatus = UpdateStatus::Checking; return; } - else if (updaterStatus == updaterChecking && !error) + else if (updaterStatus == UpdateStatus::Checking && !error) { #if !ALLOW_UNENCRYPTED_CONNECTIONS - bool connectionEncrypted = reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(); + bool connectionEncrypted = + reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(); if (!connectionEncrypted) - return abortUpdate("The " UPDATEFILEHANDLER_FILE_NAME " file could not be downloaded using a secure connection."); + return abortUpdate("The " UPDATEFILEHANDLER_FILE_NAME + " file could not be downloaded using a secure connection."); #endif // We recieved the version info file. See what it contains. @@ -217,15 +238,15 @@ void updateHandler::replyFinished(QNetworkReply *reply) if (updateFileInfo.size() == 0) { - error = true; + error = true; errorString = "The download of ther version info file was empty."; } else { - updateFileHandler remoteFile(updateFileInfo); + UpdateFileHandler remoteFile(updateFileInfo); // Next, also load the corresponding local file - updateFileHandler localFile(updatePath + UPDATEFILEHANDLER_FILE_NAME, this->updatePath); + UpdateFileHandler localFile(updatePath + UPDATEFILEHANDLER_FILE_NAME, this->updatePath); // Now compare the two so that we can download all files that require an update. // A file will be updated if: @@ -237,7 +258,8 @@ void updateHandler::replyFinished(QNetworkReply *reply) if (downloadFiles.count() > 1) { // There are files to update besides the versioninfo.txt file. - // There is a new YUView version available. Do we ask the user first or do we just install? + // There is a new YUView version available. Do we ask the user first or do we just + // install? QSettings settings; settings.beginGroup("updates"); QString updateBehavior = settings.value("updateBehavior", "ask").toString(); @@ -252,7 +274,7 @@ void updateHandler::replyFinished(QNetworkReply *reply) // The user pressed 'update' downloadAndInstallUpdate(); else - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; } reply->deleteLater(); @@ -261,18 +283,17 @@ void updateHandler::replyFinished(QNetworkReply *reply) } } } - else if (VERSION_CHECK && !error && updaterStatus == updaterChecking) + else if (VERSION_CHECK && !error && updaterStatus == UpdateStatus::Checking) { - // We can check the github master branch to see if there is a new version - // However, we cannot automatically update - QString strReply = (QString)reply->readAll(); - + // We can check the github for a new version but we cannot automatically update + QString strReply = reply->readAll(); + // parse json QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); - QJsonArray jsonArray = jsonResponse.array(); + QJsonArray jsonArray = jsonResponse.array(); if (jsonArray.size() == 0) { - error = true; + error = true; errorString = "The returned JSON object could not be parsed."; } else @@ -281,21 +302,30 @@ void updateHandler::replyFinished(QNetworkReply *reply) if (!jsonObject.contains("tag_name")) { error = true; - errorString = "The returned JSON object does not contain version information on the latest release."; + errorString = + "The returned JSON object does not contain version information on the latest release."; } else { QString serverVersion = jsonObject["tag_name"].toString(); - QString buildVersion = QString::fromUtf8(YUVIEW_VERSION); + QString buildVersion = QString::fromUtf8(YUVIEW_VERSION); + DEBUG_UPDATE("UpdateHandler::replyFinished serverVersion " + << serverVersion << " buildVersion " << buildVersion); if (serverVersion != buildVersion) { QMessageBox msgBox; msgBox.setTextFormat(Qt::RichText); - msgBox.setInformativeText("Unfortunately your version of YUView does not support automatic updating. If you compiled YUView yourself, use GIT to pull the changes and rebuild YUView. Precompiled versions of YUView are also available on Github in the releases section: https://github.com/IENT/YUView/releases"); - msgBox.setText("A newer YUView version than the one you are currently using is available on Github."); + msgBox.setInformativeText( + "Unfortunately your version of YUView does not support automatic updating. If you " + "compiled YUView yourself, use GIT to pull the changes and rebuild YUView. Precompiled " + "versions of YUView are also available on Github in the releases section: https://github.com/IENT/YUView/" + "releases"); + msgBox.setText( + "A newer YUView version than the one you are currently using is available on Github."); msgBox.exec(); - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; reply->deleteLater(); return; } @@ -309,10 +339,13 @@ void updateHandler::replyFinished(QNetworkReply *reply) { // Inform the user about the outcome of the check because he requested the check. if (error) - return abortUpdate("An error occurred while checking for updates. Are you connected to the internet? " + errorString); + return abortUpdate( + "An error occurred while checking for updates. Are you connected to the internet? " + + errorString); else { - // The software is up to date but the user requested this check so tell him that no update is required. + // The software is up to date but the user requested this check so tell him that no update is + // required. // Get if the user activated automatic checking for a new version QSettings settings; @@ -320,13 +353,18 @@ void updateHandler::replyFinished(QNetworkReply *reply) bool checkForUpdates = settings.value("checkForUpdates", true).toBool(); if (checkForUpdates) - QMessageBox::information(mainWidget, "No update found.", "Your YUView version is up to date. YUView will check for updates every time you start the application."); + QMessageBox::information(mainWidget, + "No update found.", + "Your YUView version is up to date. YUView will check for updates " + "every time you start the application."); else { // Suggest to activate automatic update checking QMessageBox msgBox(mainWidget); msgBox.setText("Your YUView version is up to date."); - msgBox.setInformativeText("Currently, automatic checking for updates is disabled. If you want to obtain the latest bugfixes and enhancements, we recommend to activate automatic update checks."); + msgBox.setInformativeText( + "Currently, automatic checking for updates is disabled. If you want to obtain the latest " + "bugfixes and enhancements, we recommend to activate automatic update checks."); msgBox.setCheckBox(new QCheckBox("Check for updates")); msgBox.exec(); @@ -341,21 +379,21 @@ void updateHandler::replyFinished(QNetworkReply *reply) } else { - forceUpdate = false; + forceUpdate = false; userCheckRequest = false; - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; } - + reply->deleteLater(); - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; } -void updateHandler::downloadAndInstallUpdate() +void UpdateHandler::downloadAndInstallUpdate() { if (!UPDATE_FEATURE_ENABLE) return; - assert(updaterStatus == updaterChecking); + assert(updaterStatus == UpdateStatus::Checking); // On windows: Before we perform the update, we restart YUView with elevated rights. // This is most likely necessary because YUView is normally installed in the 'Program Files' @@ -367,17 +405,18 @@ void updateHandler::downloadAndInstallUpdate() } // The next step is to download the update files. - updaterStatus = updaterDownloading; + updaterStatus = UpdateStatus::Downloading; // Create a progress dialog. // downloadProgress is a weak pointer since the dialog's lifetime is managed by the mainWidget. assert(downloadProgress.isNull()); assert(!mainWidget.isNull()); // dialog would leak otherwise - downloadProgress = new QProgressDialog("Downloading YUView Update...", "Cancel", 0, 100, mainWidget); + downloadProgress = + new QProgressDialog("Downloading YUView Update...", "Cancel", 0, 100, mainWidget); downloadProgress->setWindowModality(Qt::WindowModal); // Get the total download size - totalDownloadSize = 0; + totalDownloadSize = 0; currentDownloadProgress = 0; for (downloadFile f : downloadFiles) totalDownloadSize += f.second; @@ -388,21 +427,31 @@ void updateHandler::downloadAndInstallUpdate() downloadNextFile(); } -void updateHandler::restartYUView(bool elevated) +void UpdateHandler::restartYUView(bool elevated) { #ifdef Q_OS_WIN - QString executable = QCoreApplication::applicationFilePath(); - LPCWSTR fullPathToExe = (const wchar_t*) executable.utf16(); + QString executable = QCoreApplication::applicationFilePath(); + LPCWSTR fullPathToExe = (const wchar_t *)executable.utf16(); // This should trigger the UAC dialog to start the application with elevated rights. - // The "updateElevated" parameter tells the new instance of YUView that it should have elevated rights now - // and it should retry to update. + // The "updateElevated" parameter tells the new instance of YUView that it should have elevated + // rights now and it should retry to update. HINSTANCE h; if (elevated) - h = ShellExecute(nullptr, L"runas", fullPathToExe, useAlternativeSources ? L"updateElevatedAltSource" : L"updateElevated", nullptr, SW_SHOWNORMAL); + h = ShellExecute(nullptr, + L"runas", + fullPathToExe, + useAlternativeSources ? L"updateElevatedAltSource" : L"updateElevated", + nullptr, + SW_SHOWNORMAL); else - h = ShellExecute(nullptr, L"open", fullPathToExe, useAlternativeSources ? L"updateElevatedAltSource" : L"updateElevated", nullptr, SW_SHOWNORMAL); + h = ShellExecute(nullptr, + L"open", + fullPathToExe, + useAlternativeSources ? L"updateElevatedAltSource" : L"updateElevated", + nullptr, + SW_SHOWNORMAL); INT_PTR retVal = (INT_PTR)h; - if (retVal > 32) // From MSDN: If the function succeeds, it returns a value greater than 32. + if (retVal > 32) // From MSDN: If the function succeeds, it returns a value greater than 32. { // The user allowed restarting YUView as admin. Quit this one. The other one will take over. QApplication::quit(); @@ -411,21 +460,22 @@ void updateHandler::restartYUView(bool elevated) { DWORD err = GetLastError(); if (err == ERROR_CANCELLED) - return abortUpdate("YUView could not be started with admin rights. These are needed in order to update the application."); + return abortUpdate("YUView could not be started with admin rights. These are needed in order " + "to update the application."); } #else (void)elevated; #endif } -void updateHandler::abortUpdate(QString errorMsg) +void UpdateHandler::abortUpdate(QString errorMsg) { QMessageBox::critical(mainWidget, "Update error", errorMsg); // Reset the updater to initial values - forceUpdate = false; + forceUpdate = false; userCheckRequest = false; - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; if (downloadProgress) { delete downloadProgress; @@ -433,13 +483,13 @@ void updateHandler::abortUpdate(QString errorMsg) } } -void updateHandler::updateDownloadProgress(int64_t val, int64_t) +void UpdateHandler::updateDownloadProgress(int64_t val, int64_t) { if (downloadProgress) downloadProgress->setValue(currentDownloadProgress + val); } -void updateHandler::downloadNextFile() +void UpdateHandler::downloadNextFile() { if (downloadFiles.isEmpty()) return; @@ -453,29 +503,35 @@ void updateHandler::downloadNextFile() currentDownloadFile.first[i] = '/'; } - DEBUG_UPDATE("updateHandler::downloadNextFile " << currentDownloadFile.first); + DEBUG_UPDATE("UpdateHandler::downloadNextFile " << currentDownloadFile.first); QString fullURL; if (useAlternativeSources) fullURL = UPDATEFILEHANDLER_TESTDEPLOY_URL + currentDownloadFile.first; else fullURL = UPDATEFILEHANDLER_URL + currentDownloadFile.first; QNetworkReply *reply = networkManager.get(QNetworkRequest(QUrl(fullURL))); - connect(reply, &QNetworkReply::downloadProgress, this, &updateHandler::updateDownloadProgress); + connect(reply, &QNetworkReply::downloadProgress, this, &UpdateHandler::updateDownloadProgress); } -void updateHandler::downloadFinished(QNetworkReply *reply) +void UpdateHandler::downloadFinished(QNetworkReply *reply) { if (!UPDATE_FEATURE_ENABLE) return; - bool error = (reply->error() != QNetworkReply::NoError); - auto err = reply->error(); + bool error = (reply->error() != QNetworkReply::NoError); + auto err = reply->error(); bool downloadEncrypted = reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(); - DEBUG_UPDATE("updateHandler::downloadFinished " << (error ? "error " : "") << (downloadEncrypted ? "encrypted " : "not encrypted ") << reply->error()); + DEBUG_UPDATE("UpdateHandler::downloadFinished " + << (error ? "error " : "") << (downloadEncrypted ? "encrypted " : "not encrypted ") + << reply->error()); if (error) - return abortUpdate(QString("An error occurred while downloading file %1. Error code %2 (%3).").arg(currentDownloadFile.first).arg(err).arg(reply->errorString())); + return abortUpdate(QString("An error occurred while downloading file %1. Error code %2 (%3).") + .arg(currentDownloadFile.first) + .arg(err) + .arg(reply->errorString())); else if (!downloadEncrypted) - return abortUpdate(QString("File %1 could not be downloaded through a secure connection.").arg(currentDownloadFile.first)); + return abortUpdate(QString("File %1 could not be downloaded through a secure connection.") + .arg(currentDownloadFile.first)); else { // A file was downloaded successfully. Get the data. @@ -497,26 +553,31 @@ void updateHandler::downloadFinished(QNetworkReply *reply) if (!oldFile.remove()) { // Deleting the file failed. Let's just rename it to "something_old.ext" - QString newName = fileInfo.baseName() + "_old." + fileInfo.completeSuffix(); + QString newName = fileInfo.baseName() + "_old." + fileInfo.completeSuffix(); QString renamedFilePath = updatePath + newName; // First, check if this _old file already exists. If yes, delete it first. QFileInfo newFileInfo(renamedFilePath); if (newFileInfo.isFile() && newFileInfo.exists()) if (!QFile(renamedFilePath).remove()) - return abortUpdate(QString("YUView was unable to remove the file %1.").arg(renamedFilePath)); + return abortUpdate( + QString("YUView was unable to remove the file %1.").arg(renamedFilePath)); if (!oldFile.rename(newName)) - return abortUpdate(QString("YUView was unable to remove or rename the file %1.").arg(fileInfo.fileName())); - DEBUG_UPDATE("updateHandler::downloadFinished The old file could not be deleted but was renamed to " << newName); + return abortUpdate( + QString("YUView was unable to remove or rename the file %1.").arg(fileInfo.fileName())); + DEBUG_UPDATE( + "UpdateHandler::downloadFinished The old file could not be deleted but was renamed to " + << newName); } else - DEBUG_UPDATE("updateHandler::downloadFinished Successfully deleted old file " << fileInfo.fileName()); + DEBUG_UPDATE("UpdateHandler::downloadFinished Successfully deleted old file " + << fileInfo.fileName()); } // Second check: Is the file located in a subirectory that does not exist? // If so, create that subdirectory. if (currentDownloadFile.first.contains("/")) { - int lastIdx = currentDownloadFile.first.lastIndexOf("/"); + int lastIdx = currentDownloadFile.first.lastIndexOf("/"); QString fullDir = updatePath + currentDownloadFile.first.left(lastIdx); if (!QDir().mkpath(fullDir)) return abortUpdate(QString("Could not create the subdirectory %1").arg(fullDir)); @@ -525,17 +586,22 @@ void updateHandler::downloadFinished(QNetworkReply *reply) // The old file does not exist (anymore) and we can write the new file. QFile newFile(fullPath); if (!newFile.open(QIODevice::WriteOnly)) - return abortUpdate(QString("Could not open the file %1 locally for writing.").arg(currentDownloadFile.first)); + return abortUpdate( + QString("Could not open the file %1 locally for writing.").arg(currentDownloadFile.first)); else { newFile.write(data); newFile.close(); - DEBUG_UPDATE("updateHandler::downloadFinished Written downloaded data to " << currentDownloadFile.first); + DEBUG_UPDATE("UpdateHandler::downloadFinished Written downloaded data to " + << currentDownloadFile.first); if (downloadFiles.isEmpty()) { // No more files to download. Update successfully. - QMessageBox::information(mainWidget, "Update successfully.", "Update was successful. We will now start the new version of YUView."); + QMessageBox::information( + mainWidget, + "Update successfully.", + "Update was successful. We will now start the new version of YUView."); // Disconnect/delete the update progress dialog. if (downloadProgress) @@ -543,7 +609,7 @@ void updateHandler::downloadFinished(QNetworkReply *reply) delete downloadProgress; downloadProgress = NULL; } - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; // Start the new downloaded YUVeiw version. restartYUView(true); @@ -561,14 +627,14 @@ void updateHandler::downloadFinished(QNetworkReply *reply) } } -void updateHandler::forceUpdateElevated() +void UpdateHandler::forceUpdateElevated() { //// Wait. Use this code to attach a debugger to the new YUView instance with elevated rights. - //bool wait = true; - //while(wait == true) + // bool wait = true; + // while(wait == true) //{ - // QThread::sleep(1); - //} + // QThread::sleep(1); + // } if (UPDATE_FEATURE_ENABLE && is_Q_OS_WIN) { @@ -576,47 +642,3 @@ void updateHandler::forceUpdateElevated() startCheckForNewVersion(false, true); } } - -// ------------------ UpdateDialog ----------------- - -UpdateDialog::UpdateDialog(QWidget *parent) : - QDialog(parent) -{ - ui.setupUi(this); - - // Load the update settings from the QSettings - QSettings settings; - settings.beginGroup("updates"); - bool checkForUpdates = settings.value("checkForUpdates", true).toBool(); - QString updateBehavior = settings.value("updateBehavior", "ask").toString(); - settings.endGroup(); - - ui.checkUpdatesGroupBox->setChecked(checkForUpdates); - if (updateBehavior == "ask") - ui.updateSettingComboBox->setCurrentIndex(1); - else if (updateBehavior == "auto") - ui.updateSettingComboBox->setCurrentIndex(0); - - connect(ui.cancelButton, &QPushButton::clicked, this, &QDialog::reject); - - if (!UPDATE_FEATURE_ENABLE) - // If the update feature is not available, we will grey this out. - ui.updateSettingComboBox->setEnabled(false); -} - -void UpdateDialog::on_updateButton_clicked() -{ - // The user wants to download/install the update. - - // First save the settings - QSettings settings; - settings.beginGroup("updates"); - settings.setValue("checkForUpdates", ui.checkUpdatesGroupBox->isChecked()); - QString updateBehavior = "ask"; - if (ui.updateSettingComboBox->currentIndex() == 0) - updateBehavior = "auto"; - settings.setValue("updateBehavior", updateBehavior); - - // The update request was accepted by the user - accept(); -} diff --git a/YUViewLib/src/handler/update/UpdateHandler.h b/YUViewLib/src/handler/update/UpdateHandler.h new file mode 100644 index 000000000..c5c0fd241 --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateHandler.h @@ -0,0 +1,131 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * 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. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * 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 . + */ + +#pragma once + +#include + +#include +#include + +class QNetworkReply; +class QProgressDialog; + +/* The update handler does what it's name suggestes. It handles updates for YUView. + * Updates are enabled if UPDATE_FEATURE_ENABLE is set to 1. In order for automatic + * updates to work, different compilations of YUView must not be mixed. Therefor, + * the UPDATE_FEATURE_ENABLE flag is set by out buildbot before compilation. The resulting + * binary files are then put on github so that the updater can donwload them from there. + * + * The first step is to establish a list of files that we need to download/update. For this, + * we download the file 'versioninfo.txt' from github. We then compare that to the local + * 'versioninfo.txt' file to get a list of files that need to be downloaded. + * + * On windows, we need administrative rights to write to the 'Program Files' folder or even + * to rename files. So first, we restart YUView with elevated rights and the command line + * argument 'updateElevated'. This argument will let YUView know, to immediately perform the + * update without asking the user again. + * + * The update process itself then works like this: We remove the files that need updating + * and download the new versions. If a file can not be removed, we rename it to "Something_old.ext". + * If the _old file already exists, it is left from a previous update and we should be able + * to delete it now. When everything is done, we restart YUView one final time to start the + * now updated version of YUView. + */ +class UpdateHandler : public QObject +{ + Q_OBJECT + +public: + // Construct a new update handler. The mainWindows pointer is used if a dialog is shown. + UpdateHandler(QWidget *mainWindow, bool useAlternativeSources); + +public slots: + // Send the request to check for a new version of YUView + void startCheckForNewVersion(bool userRequest = true, bool force = false); + + // The windows process should have elevated rights now and we can do the update + void forceUpdateElevated(); + +private slots: + void replyFinished(QNetworkReply *reply); + void downloadFinished(QNetworkReply *reply); + void updateDownloadProgress(int64_t val, int64_t max); + void sslErrors(QNetworkReply *reply, const QList &errors); + +private: + void downloadAndInstallUpdate(); + void restartYUView(bool elevated); + + // Abort the update (reset updaterStatus to idle and show a QMessageBox::critical with the given + // message) + void abortUpdate(QString errorMsg); + + QPointer mainWidget; + QNetworkAccessManager networkManager; + + QPointer downloadProgress; + + enum class UpdateStatus + { + Idle, + EstablishConnection, + Checking, + Downloading + }; + UpdateStatus updaterStatus{UpdateStatus::Idle}; + + //< The request has been issued by the user. + bool userCheckRequest{false}; + // On windows this can indicate if the process should have elevated rights + bool elevatedRights{false}; + // If an update is availabe and this is set, we will just install the update no matter what + bool forceUpdate{false}; + // Use the alternative (test) source to get the update files + bool useAlternativeSources{false}; + + // The list or remote files we are downloading. For each file, we keep the path and name and it's + // size in bytes. + QList> downloadFiles; + + // Initiate the download of the next file. + void downloadNextFile(); + // The full name (including subdirs) and size of the file being downloaded currently + QPair currentDownloadFile; + + // When downloading files is started, these contains the size (in bytes) of all files to be + // downloaded and the current amount of bytes that were already downloaded. + int totalDownloadSize {}; + int currentDownloadProgress {}; + + QString updatePath{}; +}; diff --git a/YUViewLib/src/ui/Mainwindow.cpp b/YUViewLib/src/ui/Mainwindow.cpp index e20940931..99deab68e 100644 --- a/YUViewLib/src/ui/Mainwindow.cpp +++ b/YUViewLib/src/ui/Mainwindow.cpp @@ -61,7 +61,7 @@ MainWindow::MainWindow(bool useAlternativeSources, QWidget *parent) : QMainWindo ui.setupUi(this); // Create the update handler - updater.reset(new updateHandler(this, useAlternativeSources)); + updater = std::make_unique(this, useAlternativeSources); setFocusPolicy(Qt::StrongFocus); diff --git a/YUViewLib/src/ui/Mainwindow.h b/YUViewLib/src/ui/Mainwindow.h index cf26eb2a3..36b0f6e43 100644 --- a/YUViewLib/src/ui/Mainwindow.h +++ b/YUViewLib/src/ui/Mainwindow.h @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include