diff --git a/YUViewLib/src/common/Functions.cpp b/YUViewLib/src/common/Functions.cpp index 534802bbb..e8d8c33ab 100644 --- a/YUViewLib/src/common/Functions.cpp +++ b/YUViewLib/src/common/Functions.cpp @@ -264,4 +264,13 @@ std::optional toInt(const std::string_view text) return value; } +std::optional toInt(const QString &str) +{ + bool ok{}; + const auto value = str.toInt(&ok); + if (ok) + return value; + return {}; +} + } // namespace functions diff --git a/YUViewLib/src/common/Functions.h b/YUViewLib/src/common/Functions.h index 39aad04af..141270cbe 100644 --- a/YUViewLib/src/common/Functions.h +++ b/YUViewLib/src/common/Functions.h @@ -81,6 +81,7 @@ template QStringList toQStringList(const std::array toInt(const std::string_view str); +std::optional toInt(const QString &str); std::vector splitString(const std::string_view str, const char delimiter); std::string_view stripWhitespace(std::string_view str); 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/UpdateHandlerFile.h b/YUViewLib/src/handler/UpdateHandlerFile.h deleted file mode 100644 index 1933efb6f..000000000 --- a/YUViewLib/src/handler/UpdateHandlerFile.h +++ /dev/null @@ -1,82 +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 - -typedef QPair downloadFile; - -class updateFileHandler -{ -public: - 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. - void readFromFile(QString fileName); - - // Parse the remote file from the given QByteArray. Remote files will not be checked for - // existence on the remote side. We assume that all files listed in the remote file list - // also exist on the remote side and can be downloaded. - void readRemoteFromData(QByteArray &arr); - - // Parse one line from the update file list (remote or local) - void parseOneLine(QString &line, bool checkExistence=false); - - // 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; - - QString getInfo() const; - -private: - // For every entry in the "versioninfo.txt" file, these entries present: - struct fileListEntry - { - QString filePath; //< The file name and path - QString version; //< The version of the file - QString hash; //< A hash of the file (unused so far) - int fileSize; //< The size of the file in bytes - }; - fileListEntry createFileEntry(QStringList &lineSplit) const; - - // For every entry, there is the file path/name and a version string - QList updateFileList; - QString updatePath{}; - bool loaded {false}; -}; diff --git a/YUViewLib/src/handler/update/UpdateDialog.cpp b/YUViewLib/src/handler/update/UpdateDialog.cpp new file mode 100644 index 000000000..af008e7d3 --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateDialog.cpp @@ -0,0 +1,83 @@ +/* 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 + +namespace update +{ + +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(); +} + +} // namespace update diff --git a/YUViewLib/src/handler/update/UpdateDialog.h b/YUViewLib/src/handler/update/UpdateDialog.h new file mode 100644 index 000000000..47c4037f0 --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateDialog.h @@ -0,0 +1,55 @@ +/* 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" + +namespace update +{ + +// 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; +}; + +} // namespace update diff --git a/YUViewLib/src/handler/UpdateHandlerFile.cpp b/YUViewLib/src/handler/update/UpdateFileHandler.cpp similarity index 50% rename from YUViewLib/src/handler/UpdateHandlerFile.cpp rename to YUViewLib/src/handler/update/UpdateFileHandler.cpp index 034e849c7..540f82969 100644 --- a/YUViewLib/src/handler/UpdateHandlerFile.cpp +++ b/YUViewLib/src/handler/update/UpdateFileHandler.cpp @@ -1,36 +1,36 @@ /* 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 "UpdateHandlerFile.h" + * + * 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 "UpdateFileHandler.h" #include #include @@ -43,31 +43,35 @@ #define DEBUG_UPDATE_FILE(msg) ((void)0) #endif -const auto UPDATEFILEHANDLER_FILE_NAME = "versioninfo.txt"; +namespace update +{ -updateFileHandler::updateFileHandler() -{} +const auto UpdateFileHandler_FILE_NAME = "versioninfo.txt"; -updateFileHandler::updateFileHandler(QString fileName, QString updatePath) : - updatePath(updatePath) -{ - this->readFromFile(fileName); +UpdateFileHandler::UpdateFileHandler() +{ } -updateFileHandler::updateFileHandler(QByteArray &byteArray) -{ - this->readRemoteFromData(byteArray); +UpdateFileHandler::UpdateFileHandler(QString fileName, QString updatePath) : updatePath(updatePath) +{ + this->readFromFile(fileName); +} + +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; } @@ -86,22 +90,22 @@ void updateFileHandler::readFromFile(QString fileName) } this->loaded = true; } - -void updateFileHandler::readRemoteFromData(QByteArray &arr) + +void UpdateFileHandler::readRemoteFromData(QByteArray &arr) { - const QString reply = QString(arr); + const QString reply = QString(arr); const QStringList lines = reply.split("\n"); for (auto line : lines) 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 # @@ -110,7 +114,7 @@ void updateFileHandler::parseOneLine(QString &line, bool checkExistence) if (lineSplit.count() == 4) { auto entry = createFileEntry(lineSplit); - + if (checkExistence) { // Check if the file exists locally @@ -119,28 +123,31 @@ void updateFileHandler::parseOneLine(QString &line, bool checkExistence) this->updateFileList.append(entry); 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."); + // 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."); } else // Do not check if the file exists this->updateFileList.append(entry); } } - -QList updateFileHandler::getFilesToUpdate(updateFileHandler &localFiles) const + +QList UpdateFileHandler::getFilesToUpdate(UpdateFileHandler &localFiles) const { QList updateList; for (auto remoteFile : this->updateFileList) { - bool fileFound = false; + bool fileFound = false; bool updateNeeded = false; - for(auto localFile : localFiles.updateFileList) + for (auto localFile : localFiles.updateFileList) { if (localFile.filePath.toLower() == remoteFile.filePath.toLower()) { // File found. Do we need to update it? - updateNeeded = (localFile.version != remoteFile.version) || (localFile.hash != remoteFile.hash); + updateNeeded = + (localFile.version != remoteFile.version) || (localFile.hash != remoteFile.hash); fileFound = true; break; } @@ -149,11 +156,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,10 +170,10 @@ QString updateFileHandler::getInfo() const return s; } -updateFileHandler::fileListEntry updateFileHandler::createFileEntry(QStringList &lineSplit) const +UpdateFileHandler::fileListEntry UpdateFileHandler::createFileEntry(QStringList &lineSplit) const { fileListEntry entry; - + entry.filePath = lineSplit[0]; if (entry.filePath.endsWith(",")) // There is a comma at the end. Remove it. @@ -181,6 +188,8 @@ updateFileHandler::fileListEntry updateFileHandler::createFileEntry(QStringList if (sizeString.endsWith(",")) sizeString.chop(1); entry.fileSize = sizeString.toInt(); - + return entry; } + +} // namespace update diff --git a/YUViewLib/src/handler/update/UpdateFileHandler.h b/YUViewLib/src/handler/update/UpdateFileHandler.h new file mode 100644 index 000000000..2ff0ebd33 --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateFileHandler.h @@ -0,0 +1,87 @@ +/* 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 + +namespace update +{ + +typedef QPair downloadFile; + +class UpdateFileHandler +{ +public: + 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. + void readFromFile(QString fileName); + + // Parse the remote file from the given QByteArray. Remote files will not be checked for + // existence on the remote side. We assume that all files listed in the remote file list + // also exist on the remote side and can be downloaded. + void readRemoteFromData(QByteArray &arr); + + // Parse one line from the update file list (remote or local) + void parseOneLine(QString &line, bool checkExistence = false); + + // 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; + + QString getInfo() const; + +private: + // For every entry in the "versioninfo.txt" file, these entries present: + struct fileListEntry + { + QString filePath; //< The file name and path + QString version; //< The version of the file + QString hash; //< A hash of the file (unused so far) + int fileSize; //< The size of the file in bytes + }; + fileListEntry createFileEntry(QStringList &lineSplit) const; + + // For every entry, there is the file path/name and a version string + QList updateFileList; + QString updatePath{}; + bool loaded{false}; +}; + +} // namespace update diff --git a/YUViewLib/src/handler/UpdateHandler.cpp b/YUViewLib/src/handler/update/UpdateHandler.cpp similarity index 51% rename from YUViewLib/src/handler/UpdateHandler.cpp rename to YUViewLib/src/handler/update/UpdateHandler.cpp index ff0b31690..a391cf70c 100644 --- a/YUViewLib/src/handler/UpdateHandler.cpp +++ b/YUViewLib/src/handler/update/UpdateHandler.cpp @@ -1,38 +1,40 @@ /* 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 "VersionComparison.h" #include #include @@ -42,8 +44,8 @@ #include #include #include -#include #include +#include #include #include @@ -67,48 +69,57 @@ #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) +namespace update +{ + +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 +128,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 commit hash. 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/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 { // 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 +242,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 +262,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 +278,7 @@ void updateHandler::replyFinished(QNetworkReply *reply) // The user pressed 'update' downloadAndInstallUpdate(); else - updaterStatus = updaterIdle; + updaterStatus = UpdateStatus::Idle; } reply->deleteLater(); @@ -261,41 +287,46 @@ 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 { 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) + const auto serverVersion = jsonObject["tag_name"].toString(); + if (isServerVersionNewer(serverVersion, QString::fromUtf8(YUVIEW_VERSION))) { 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 +340,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 +354,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 +380,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 +406,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 +428,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 +461,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 +484,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 +504,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 +554,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 +587,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 +610,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 +628,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) { @@ -577,46 +644,4 @@ void updateHandler::forceUpdateElevated() } } -// ------------------ 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(); -} +} // namespace update diff --git a/YUViewLib/src/handler/update/UpdateHandler.h b/YUViewLib/src/handler/update/UpdateHandler.h new file mode 100644 index 000000000..f4029c17d --- /dev/null +++ b/YUViewLib/src/handler/update/UpdateHandler.h @@ -0,0 +1,136 @@ +/* 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; + +namespace update +{ + +/* 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{}; +}; + +} // namespace update diff --git a/YUViewLib/src/handler/update/VersionComparison.cpp b/YUViewLib/src/handler/update/VersionComparison.cpp new file mode 100644 index 000000000..e0598b1c7 --- /dev/null +++ b/YUViewLib/src/handler/update/VersionComparison.cpp @@ -0,0 +1,132 @@ +/* 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 "VersionComparison.h" + +#include + +#include + +#define VERSIONCOMPARE_DEBUG_OUTPUT 0 +#if UPDATER_DEBUG_OUTPUT && !NDEBUG +#include +#define DEBUG_VERSION(msg) qDebug() << msg +#else +#define DEBUG_VERSION(msg) ((void)0) +#endif + +namespace update +{ + +namespace +{ + +struct Version +{ + int major{}; + int minor{}; + int min{}; + bool hasAdditionalTag{}; + + bool operator>(const Version &other) const + { + if (this->major > other.major) + return true; + if (this->major == other.major) + { + if (this->minor > other.minor) + return true; + if (this->minor == other.minor) + { + if (this->min > other.min) + return true; + if (this->min == other.min) + return this->hasAdditionalTag && !other.hasAdditionalTag; + } + } + + return false; + } +}; + +std::optional parseVersionFromString(QString str) +{ + QRegularExpression versionRegexp( + "^[vV]?(?\\d+)(.(?\\d+))?(.(?\\d+))?(?-[a-zA-Z0-9]+)?$"); + auto versionMatch = versionRegexp.match(str); + if (!versionMatch.hasMatch()) + return {}; + + Version version; + if (const auto major = functions::toInt(versionMatch.captured("major"))) + version.major = *major; + else + return {}; + + version.minor = functions::toInt(versionMatch.captured("minor")).value_or(0); + version.min = functions::toInt(versionMatch.captured("min")).value_or(0); + version.hasAdditionalTag = !versionMatch.captured("tag").isEmpty(); + + return version; +} + +} // namespace + +bool isServerVersionNewer(const QString &serverVersionString, const QString ¤tVersionString) +{ + const auto currentVersion = parseVersionFromString(currentVersionString); + if (!currentVersion) + { + DEBUG_VERSION("isServerVersionNewer Error parsing current version from string " + << YUVIEW_VERSION); + return false; + } + + const auto serverVersion = parseVersionFromString(serverVersionString); + if (!serverVersion) + { + DEBUG_VERSION("isServerVersionNewer Error parsing server version from string " + << serverVersionString); + return false; + } + + DEBUG_VERSION("isServerVersionNewer serverVersion v" + << serverVersion.major << "." << serverVersion.minor << "." << serverVersion.min + << (serverVersion.hasAdditionalTag ? "-tag" : "") << " currentVersion " + << currentVersion.major << "." << currentVersion.minor << "." << currentVersion.min + << (currentVersion.hasAdditionalTag ? "-tag" : "")); + + // This is the current logic + return *serverVersion > *currentVersion; +} + +} // namespace update diff --git a/YUViewLib/src/handler/update/VersionComparison.h b/YUViewLib/src/handler/update/VersionComparison.h new file mode 100644 index 000000000..e5ab1f017 --- /dev/null +++ b/YUViewLib/src/handler/update/VersionComparison.h @@ -0,0 +1,42 @@ +/* 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 + +namespace update +{ + +bool isServerVersionNewer(const QString &serverVersion, const QString ¤tVersion); + +} diff --git a/YUViewLib/src/ui/Mainwindow.cpp b/YUViewLib/src/ui/Mainwindow.cpp index e20940931..37c4207dc 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..f389eb9c8 100644 --- a/YUViewLib/src/ui/Mainwindow.h +++ b/YUViewLib/src/ui/Mainwindow.h @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include