# HG changeset patch # User Andre Heinecke # Date 1400604028 0 # Node ID 15121735805eb2f00ba2c7a6e904c99195fce3fd # Parent 0ec3516bf65c62105125c8630585b9897636883f Start implementation of software update installation diff -r 0ec3516bf65c -r 15121735805e ui/mainwindow.cpp --- a/ui/mainwindow.cpp Tue May 20 16:39:51 2014 +0000 +++ b/ui/mainwindow.cpp Tue May 20 16:40:28 2014 +0000 @@ -8,6 +8,7 @@ #include "mainwindow.h" #include +#include #include #include #include @@ -35,7 +36,7 @@ #define SERVER_URL "https://files.intevation.de:443" #define LIST_RESOURCE "/users/aheinecke/zertifikatsliste.txt" -#define SW_RESOURCE "/users/aheinecke/zertifikatsliste.txt" +#define SW_RESOURCE "/users/aheinecke/trustbridge.exe" #ifdef Q_OS_WIN #define SW_RESOURCE_VERSION "/users/aheinecke/trustbridge-%1.exe" #else @@ -50,6 +51,7 @@ #include "separatoritemdelegate.h" #include "installwrapper.h" #include "util.h" +#include "logging.h" MainWindow::MainWindow(bool trayMode): mTrayMode(trayMode) @@ -97,6 +99,11 @@ if (mCurState == NewListAvailable) { show(); } + + if (mCurState == NewSoftwareAvailable) { + checkUpdates(true); + mCurState = DownloadingSW; + } } void MainWindow::showMessage() @@ -148,7 +155,7 @@ } if (!swFileName.isEmpty()) { - // TODO + // TODO Verify integrity of the software } else { mSettings.remove("Software/available"); mSettings.remove("Software/availableDate"); @@ -176,7 +183,8 @@ } void MainWindow::handleNewSW(const QString& fileName, const QDateTime& modDate) { - mCurMessage = tr("An update for %1 is available. Click here to install.").arg( + mCurMessage = tr("

An update for %1 is available.

\n" + "Click here to download and install the update.").arg( QApplication::applicationName()); setState(NewSoftwareAvailable); mSettings.setValue("Software/available", fileName); @@ -186,12 +194,61 @@ showMessage(); } +void MainWindow::installNewSW(const QString& fileName, const QDateTime& modDate) { + QFileInfo instProcInfo = QFileInfo(fileName); + if (!instProcInfo.isExecutable()) { + qWarning() << "Downloaded file: " << fileName << " is not executable."; + setState(TransferError); + return; + } +#ifdef WIN32 + SHELLEXECUTEINFOW shExecInfo; + shExecInfo.lpFile = reinterpret_cast (fileName.utf16()); + + if (!is_admin()) { + shExecInfo.lpVerb = L"open"; + } else { + shExecInfo.lpVerb = L"runas"; + } + + qDebug() << "Starting process " << fileName; + + if (!ShellExecuteExW(&shExecInfo)) { + /* Execution failed, maybe the user aborted the UAC check? */ + char* errmsg = getLastErrorMsg(); + QString qerrmsg = QString::fromUtf8(errmsg); + free(errmsg); + qDebug() << "Failed to start process: " << qerrmsg; + setState(NewSoftwareAvailable); + return; + } + +#else /* WIN32 */ + QProcess installerProcess; + installerProcess.setProgram(fileName); + + qDebug() << "Starting process " << fileName; + + if (!installerProcess.waitForStarted() || + installerProcess.state() == QProcess::NotRunning) { + qDebug() << "Failed to start process."; + return; + } +#endif + /* Installer process should now be running. We exit */ + + closeApp(); +} + void MainWindow::checkUpdates(bool downloadSW) { verifyAvailableData(); - if (!mSettings.contains("Software/installedDate")) { - lookUpDateForVersion(); + if (!mSettings.contains("Software/installedDate") || + mSettings.value("Software/installedVersion").toString() != QApplication::applicationVersion()) { + /* This should only happen on initial startup and after an update has + * been installed */ + getLastModForCurrentVersion(); return; } QDateTime listInstalledLastMod = mSettings.value("List/installedDate").toDateTime(); @@ -210,8 +267,7 @@ Downloader* downloader = new Downloader(this, QString::fromLatin1(SERVER_URL), QByteArray(), - QDateTime::currentDateTime(), -// TODO swInstalledLastMod, + swInstalledLastMod, listInstalledLastMod, swResource, listResource, @@ -221,19 +277,19 @@ this, SLOT(handleNewList(const QString&, const QDateTime&))); if (!downloadSW) { connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), - this, SLOT(newSWAvailable(const QString&, const QDateTime&))); + this, SLOT(handleNewSW(const QString&, const QDateTime&))); + } else { + connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), + this, SLOT(installNewSW(const QString&, const QDateTime&))); } - else { - connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), - this, SLOT(handleNewSW(const QString&, const QDateTime&))); - } + connect(downloader, SIGNAL(finished()), downloader, SLOT(deleteLater())); connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)), this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode))); downloader->start(); } -void MainWindow::lookUpDateForVersion() +void MainWindow::getLastModForCurrentVersion() { QString softwareVersion = QString::fromLatin1(SW_RESOURCE_VERSION).arg( QApplication::applicationVersion()); @@ -251,39 +307,28 @@ connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)), this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode))); connect(downloader, SIGNAL(lastModifiedDate(const QDateTime&)), - this, SLOT(setLastModifiedDate(const QDateTime&))); + this, SLOT(setLastModifiedSWDate(const QDateTime&))); downloader->start(); } -void MainWindow::setLastModifiedDate(const QDateTime &date) +void MainWindow::setLastModifiedSWDate(const QDateTime &date) { mSettings.beginGroup("Software"); mSettings.setValue("installedDate", date); + mSettings.setValue("installedVersion", QApplication::applicationVersion()); mSettings.endGroup(); checkUpdates(); } -void MainWindow::newSWAvailable(const QString &fileName, const QDateTime &date) { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Information); - msgBox.setText("

" + tr("New Software version is available.") + "

"); - msgBox.setInformativeText(tr("Do you want to install the new Version?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Yes); - int selection = msgBox.exec(); - if (selection == QMessageBox::Yes) { - checkUpdates(true); - } -} - void MainWindow::downloaderError(const QString &message, SSLConnection::ErrorCode error) { + /* TODO logging and handle error according to a plan */ mCurMessage = message; showMessage(); + setState(TransferError); } - void MainWindow::createActions() { mCheckUpdates = new QAction(tr("Check for Updates"), this); diff -r 0ec3516bf65c -r 15121735805e ui/mainwindow.h --- a/ui/mainwindow.h Tue May 20 16:39:51 2014 +0000 +++ b/ui/mainwindow.h Tue May 20 16:40:28 2014 +0000 @@ -54,6 +54,7 @@ BeforeDownload, NewListAvailable, NewSoftwareAvailable, + DownloadingSW, TransferError, NothingChanged }; @@ -76,13 +77,33 @@ void installerError(const QString& errMsg); void installerSuccess(); void installCerts(); - void newSWAvailable(const QString& fileName, const QDateTime& modDate); + + /* @brief Execute the file fileName to install the softwareupdate. + * + * Once the installer process is started this function terminates + * the application. */ + void installNewSW(const QString& fileName, const QDateTime& modDate); void saveAutoUpdate(int state); void saveAutoStart(int state); - void lookUpDateForVersion(); - void setLastModifiedDate(const QDateTime &date); + /** @brief get the last modified date on the download server for + * the current version. + * + * After the initial installation this function can be used to + * determine the DateTime that corresponds to the currently installed + * version on the download server. + * + * Calls setLastModifiedSWDate on success. Otherwise downloaderError + * is triggered. */ + void getLastModForCurrentVersion(); + + /** @brief set the last modified software date/time + * + * The last modifiedSWDate is the corresponding last modified + * timestamp from the download server vor the currently installed version. + */ + void setLastModifiedSWDate(const QDateTime &date); /** @brief saves the currently unselected certificates *