diff --git a/src/gui/formupdate.cpp b/src/gui/formupdate.cpp index 6e0b8a0e2..0e6db7c12 100755 --- a/src/gui/formupdate.cpp +++ b/src/gui/formupdate.cpp @@ -30,14 +30,16 @@ #include #include -#if defined(Q_OS_WIN) -#include "qt_windows.h" -#endif FormUpdate::FormUpdate(QWidget *parent) : QDialog(parent), m_ui(new Ui::FormUpdate) { m_ui->setupUi(this); +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) + m_downloader = NULL; + m_readyToInstall = false; +#endif + // Set flags and attributes. setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog); setWindowIcon(IconFactory::instance()->fromTheme("application-about")); @@ -84,6 +86,8 @@ void FormUpdate::checkForUpdates() { m_ui->m_txtChanges->setText(update.first.m_changes); if (update.first.m_availableVersion >= APP_VERSION) { + bool update_for_this_system = isUpdateForThisSystem(); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("New release available."), tr("This is new version which can be\ndownloaded and installed.")); @@ -92,6 +96,10 @@ void FormUpdate::checkForUpdates() { tr("Download installation file for your OS.") : tr("Installation file is not available directly.\n" "Go to application website to obtain it manually.")); + + if (update_for_this_system) { + m_btnUpdate->setText(tr("Download update")); + } } else { m_ui->m_lblStatus->setStatus(WidgetWithStatus::Warning, @@ -103,6 +111,74 @@ void FormUpdate::checkForUpdates() { } } +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) +void FormUpdate::updateProgress(qint64 bytes_received, qint64 bytes_total) { + qApp->processEvents(); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Information, + tr("Downloaded %1% (update size is %2 kB).").arg(QString::number((bytes_received * 100.0) / bytes_total, + 'f', + 2), + QString::number(bytes_total / 1000, + 'f', + 2)), + tr("Downloading update...")); +} + +void FormUpdate::saveUpdateFile(const QByteArray &file_contents) { + QString url_file = m_updateInfo.m_urls.value(OS_ID).m_fileUrl;; + +#if QT_VERSION >= 0x050000 + QString temp_directory = QStandardPaths::writableLocation(QStandardPaths::TempLocation); +#else + QString temp_directory = QDesktopServices::storageLocation(QDesktopServices::TempLocation); +#endif + + if (!temp_directory.isEmpty()) { + QString output_file_name = url_file.mid(url_file.lastIndexOf('/') + 1); + QFile output_file(temp_directory + QDir::separator() + output_file_name); + + if (output_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qDebug("Storing update file to temporary location '%s'.", + qPrintable(output_file_name)); + + output_file.write(file_contents); + output_file.flush(); + output_file.close(); + + qDebug("Update file contents was successfuly saved."); + + m_updateFilePath = output_file.fileName(); + m_readyToInstall = true; + } + else { + qDebug("Cannot save downloaded update file because target temporary file '%s' cannot be " + "opened for writing.", output_file_name); + } + } + else { + qDebug("Cannot save downloaded update file because no TEMP folder is available."); + } +} + +void FormUpdate::updateCompleted(QNetworkReply::NetworkError status, QByteArray contents) { + qDebug("Download of application update file was completed with code '%s'.", + status); + + switch (status) { + case QNetworkReply::NoError: + saveUpdateFile(contents); + + m_btnUpdate->setText(tr("Install update")); + m_btnUpdate->setEnabled(true); + break; + + default: + m_btnUpdate->setText(tr("Error occured")); + break; + } +} +#endif + void FormUpdate::startUpdate() { QString url_file; bool update_for_this_system = isUpdateForThisSystem(); @@ -115,35 +191,71 @@ void FormUpdate::startUpdate() { } #if defined(Q_OS_WIN) || defined(Q_OS_OS2) - Downloader *downloader = new Downloader(this); + if (m_readyToInstall) { + close(); - connect(downloader, SIGNAL(completed(QNetworkReply::NetworkError,QByteArray)), - this, SLOT(finish(QNetworkReply::NetworkError,QByteArray))); + qDebug("Preparing to launch external updater '%s'.", APP_UPDATER_EXECUTABLE); - // TODO: tady jen zavolat updater a ten by si to mohl stahnout sam. - downloader->downloadFile(url_file); -#else - if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { - if (SystemTrayIcon::isSystemTrayActivated()) { - SystemTrayIcon::instance()->showMessage(tr("Cannot update application"), - tr("Cannot navigate to installation file. Check new installation downloads " - "manually on project website."), - QSystemTrayIcon::Warning); + if (!QProcess::startDetached(APP_UPDATER_EXECUTABLE, + QStringList() << + APP_VERSION << + m_updateInfo.m_availableVersion << + QDir::toNativeSeparators(qApp->applicationFilePath()) << + QDir::toNativeSeparators(m_updateFilePath))) { + qDebug("External updater was not launched due to error."); + + if (SystemTrayIcon::isSystemTrayActivated()) { + SystemTrayIcon::instance()->showMessage(tr("Cannot update application"), + tr("Cannot launch external updater. Update application manually."), + QSystemTrayIcon::Warning); + } + else { + MessageBox::show(this, + QMessageBox::Warning, + tr("Cannot update application"), + tr("Cannot launch external updater. Update application manually.")); + } } - else { - MessageBox::show(this, - QMessageBox::Warning, - tr("Cannot update application"), - tr("Cannot navigate to installation file. Check new installation downloads " - "manually on project website.")); + + return; + } + + if (update_for_this_system) { + if (m_downloader == NULL) { + // Initialie downloader. + m_downloader = new Downloader(this); + + connect(m_downloader, SIGNAL(progress(qint64,qint64)), + this, SLOT(updateProgress(qint64,qint64))); + connect(m_downloader, SIGNAL(completed(QNetworkReply::NetworkError,QByteArray)), + this, SLOT(updateCompleted(QNetworkReply::NetworkError,QByteArray))); + } + + m_btnUpdate->setText(tr("Downloading update...")); + m_btnUpdate->setEnabled(false); + + m_downloader->downloadFile(url_file); + + } else { + if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { + if (SystemTrayIcon::isSystemTrayActivated()) { + SystemTrayIcon::instance()->showMessage(tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads " + "manually on project website."), + QSystemTrayIcon::Warning); + } + else { + MessageBox::show(this, + QMessageBox::Warning, + tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads " + "manually on project website.")); + } } } -#endif -} - -#if defined(Q_OS_WIN) || defined(Q_OS_OS2) -void FormUpdate::finish(QNetworkReply::NetworkError err, QByteArray arr) -{ + /* + * // TODO: http://pastebin.com/Lvb1bsJP + // chyba od elberta // TODO: presunou do updatera. QString url_file = m_updateInfo.m_urls.value(OS_ID).m_fileUrl;; @@ -163,27 +275,23 @@ void FormUpdate::finish(QNetworkReply::NetworkError err, QByteArray arr) output_file.close(); close(); - - QProcess::startDetached(APP_UPDATER_EXECUTABLE, - QStringList() << temp_directory <applicationFilePath() << output_file.fileName()); - - //ShellExecute(0, - // 0, - // (wchar_t *) QString(APP_UPDATER_EXECUTABLE).utf16(), - // (wchar_t *) QString("\"%1\" \"%2\" \"%3\"").arg(temp_directory, - // qApp->applicationFilePath(), - // output_file.fileName()).utf16(), - // 0, - // SW_SHOWNORMAL); - + } + }*/ +#else + if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { + if (SystemTrayIcon::isSystemTrayActivated()) { + SystemTrayIcon::instance()->showMessage(tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads " + "manually on project website."), + QSystemTrayIcon::Warning); } else { - // TODO: chyba - nelze zapisovat do souboru + MessageBox::show(this, + QMessageBox::Warning, + tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads " + "manually on project website.")); } - } - else { - // TODO: chyba - nelze ulozit soubor. - } -} #endif +} diff --git a/src/gui/formupdate.h b/src/gui/formupdate.h index 07d913e34..e26991025 100644 --- a/src/gui/formupdate.h +++ b/src/gui/formupdate.h @@ -21,6 +21,10 @@ #include #include +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) +#include +#endif + #include "ui_formupdate.h" #include "miscellaneous/systemfactory.h" @@ -30,6 +34,10 @@ namespace Ui { class FormUpdate; } +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) +class Downloader; +#endif + class FormUpdate : public QDialog { Q_OBJECT @@ -48,10 +56,16 @@ class FormUpdate : public QDialog { void startUpdate(); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) - void finish(QNetworkReply::NetworkError err, QByteArray arr); -#endif + void updateProgress(qint64 bytes_received, qint64 bytes_total); + void updateCompleted(QNetworkReply::NetworkError status, QByteArray contents); + void saveUpdateFile(const QByteArray &file_contents); private: + Downloader *m_downloader; + bool m_readyToInstall; + QString m_updateFilePath; +#endif + Ui::FormUpdate *m_ui; UpdateInfo m_updateInfo; QPushButton *m_btnUpdate; diff --git a/src/updater/main.cpp b/src/updater/main.cpp index 9eb8ce15d..9fd0b701b 100644 --- a/src/updater/main.cpp +++ b/src/updater/main.cpp @@ -34,7 +34,8 @@ bool removeDir(const QString & dirName) { QDir dir(dirName); if (dir.exists(dirName)) { - foreach (QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { + foreach (QFileInfo info, + dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { if (info.isDir()) { result = removeDir(info.absoluteFilePath()); } @@ -55,6 +56,7 @@ bool removeDir(const QString & dirName) { bool copyPath(QString src, QString dst) { QDir dir(src); + if (! dir.exists()) { return false; } @@ -71,25 +73,34 @@ bool copyPath(QString src, QString dst) { if (!QFile::exists(destination_file) || QFile::remove(destination_file)) { if (QFile::copy(src + QDir::separator() + f, destination_file)) { - qDebug("Copied %s", qPrintable(f)); + qDebug("Copied file '%s'.", qPrintable(f)); } else { - qDebug("Failed to copy file '%s'", qPrintable(original_file)); + qDebug("Failed to copy file '%s'.", qPrintable(original_file)); } } else { - qDebug("Failed to remove file '%s'", qPrintable(original_file)); + qDebug("Failed to remove file '%s'.", qPrintable(original_file)); } } return true; } +// Main entry point to "rssguard_updater.exe". +// It expects 4 ARGUMENTS: +// 0) - the actual path of this process, +// 1) - string with current version, +// 2) - string with future version, +// 3) - path to RSS Guard ("rssguard.exe") file, +// 4) - path to update file (stored in TEMP folder). int main(int argc, char *argv[]) { // Instantiate base application object. QtSingleCoreApplication application(APP_LOW_NAME, argc, argv); - if (argc != 4) { + qDebug("\n===== RSS Guard updater ====\n"); + + if (argc != 5) { qDebug("Insufficient arguments passed. Update process cannot proceed."); qDebug("Press any key to exit updater..."); @@ -98,6 +109,162 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + QString this_process_path = QDir::toNativeSeparators(application.applicationFilePath()); + QString current_version(argv[1]); + QString next_version(argv[2]); + QString rssguard_executable_path(argv[3]); + QString rssguard_path = QDir::toNativeSeparators(QFileInfo(rssguard_executable_path).absolutePath()); + QString update_file_path(argv[4]); + QString temp_path = QDir::toNativeSeparators(QFileInfo(update_file_path).absolutePath()); + QString output_temp_path = temp_path + QDir::separator() + APP_LOW_NAME; + + qDebug("Starting updater."); + qDebug("Version changes from %s to %s.", + qPrintable(current_version), + qPrintable(next_version)); + + qDebug("\n===== Files & versions ====\n"); + + qDebug("This process:\n\t %s", qPrintable(this_process_path)); + qDebug("Application executable:\n\t %s", qPrintable(rssguard_executable_path)); + qDebug("TEMP path:\n\t %s", qPrintable(temp_path)); + + qDebug("\n===== Update file metadata ====\n"); + + bool update_file_exists = QFile::exists(update_file_path); + + qDebug("Update file exists:\n\t %s", update_file_exists ? "yes" : "no"); + qDebug("Update file path:\n\t %s", qPrintable(update_file_path)); + qDebug("Update file size:\n\t %d bytes", QFileInfo(update_file_path).size()); + + if (!update_file_exists) { + qDebug("\nUpdate file does NOT exist. Updater cannot proceed."); + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + + // Check if main RSS Guard instance is running. + if (application.sendMessage(APP_QUIT_INSTANCE)) { + qDebug("RSS Guard application is running. Quitting it."); + } + + qDebug("\n===== Cleanup ====\n"); + + // Remove old folders. + if (QDir(output_temp_path).exists()) { + if (!removeDir(output_temp_path)) { + qDebug("Cleanup of old temporary files failed."); + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + } + + qDebug("Old files removed."); + + qDebug("Update files are ready. Press any key to proceed..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + QString extractor(APP_7ZA_EXECUTABLE); + QStringList extractor_arguments; + + extractor_arguments << "x" << update_file_path << "-r" << + "-y" << QString("-o%1").arg(output_temp_path); + + qDebug("\n===== Decompression =====\n"); + + switch (QProcess::execute(extractor, extractor_arguments)) { + case -1: + qDebug("\nDecompressor crashed. Upgrading process failed."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + + case -2: + qDebug("\nDecompressor was not started successfully. Upgrading process failed."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + + case 0: + qDebug("\nDecompression is done. Proceeding to copying files to application directory."); + break; + + default: + qDebug("\nUnspecified error occured."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + + qDebug("\n===== Copying =====\n"); + + // Find "rssguard" subfolder path in + QFileInfoList rssguard_temp_root = QDir(output_temp_path).entryInfoList(QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks); + + if (rssguard_temp_root.size() != 1) { + qDebug("Could not find root of downloaded application data."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + + QString rssguard_single_temp_root = rssguard_temp_root.at(0).absoluteFilePath(); + + if (!copyPath(rssguard_single_temp_root, rssguard_path)) { + qDebug("Critical error appeared during copying of application files."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + + qDebug("\n===== Cleanup =====\n"); + + removeDir(output_temp_path); + QFile::remove(update_file_path); + + qDebug("Temporary files removed."); + + qDebug("Press any key to exit updater and start RSS Guard."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + if (!QProcess::startDetached(rssguard_executable_path)) { + qDebug("RSS Guard was not started successfully. Start it manually."); + + qDebug("Press any key to exit updater..."); + + std::cin.ignore(std::numeric_limits::max(), '\n'); + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + + /* QString temp_directory = QDir::toNativeSeparators(argv[1]); QString rssguard_executable = QDir::toNativeSeparators(argv[2]); QString rssguard_path = QDir::toNativeSeparators(QFileInfo(rssguard_executable).absolutePath()); @@ -209,7 +376,6 @@ int main(int argc, char *argv[]) { std::cin.ignore(std::numeric_limits::max(), '\n'); QProcess::startDetached(rssguard_executable); - - return EXIT_SUCCESS; +*/ }