aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ aheinecke@0: #include "mainwindow.h" aheinecke@0: aheinecke@0: #include aheinecke@551: #include aheinecke@365: #include aheinecke@0: #include aheinecke@0: #include aheinecke@0: #include aheinecke@563: #include aheinecke@0: #include aheinecke@2: #include aheinecke@16: #include andre@1116: #include aheinecke@19: #include rrenkert@187: #include rrenkert@187: #include emanuel@709: #include rrenkert@187: #include rrenkert@189: #include rrenkert@205: #include rrenkert@205: #include rrenkert@250: #include rrenkert@584: #include rrenkert@584: #include andre@857: #include andre@940: #include aheinecke@19: andre@609: #include "certificatelist.h" andre@1234: #include "certificateitemwidget.h" andre@609: #include "downloader.h" andre@609: #include "helpdialog.h" andre@609: #include "aboutdialog.h" andre@609: #include "separatoritemdelegate.h" andre@609: #include "installwrapper.h" andre@609: #include "util.h" andre@609: #include "logging.h" andre@609: #include "binverify.h" andre@609: #include "processhelp.h" andre@609: #include "processwaitdialog.h" andre@871: #include "trayicon.h" andre@956: #include "proxysettingsdlg.h" andre@609: aheinecke@19: // The amount of time in minutes stay silent if we have aheinecke@19: // something to say aheinecke@548: #define NAG_INTERVAL_MINUTES 70 aheinecke@0: emanuel@1104: #define DATETIME_FORMAT "d. MMM yyyy HH:mm" andre@714: aheinecke@409: #ifndef APPNAME aheinecke@409: #define APPNAME "TrustBridge" aheinecke@409: #endif aheinecke@409: andre@994: #ifndef SERVER_URL andre@994: #error "DOWNLOAD_SERVER option not set or invalid." andre@994: #endif aheinecke@72: andre@885: #if defined(_X86_) || defined(__i386__) andre@885: #define TB_ARCH_STRING "-i386" andre@885: #else andre@885: #define TB_ARCH_STRING "-amd64" andre@885: #endif andre@885: andre@1127: #ifdef IS_TAG_BUILD andre@1088: # define LIST_RESOURCE "/zertifikatsliste.txt" andre@607: # ifdef Q_OS_WIN andre@1088: # define SW_RESOURCE_VERSION "/TrustBridge-%1.exe" andre@1088: # define SW_RESOURCE "/TrustBridge.exe" andre@607: # else andre@1088: # define SW_RESOURCE_VERSION "/TrustBridge-%1" TB_ARCH_STRING ".sh" andre@1088: # define SW_RESOURCE "/TrustBridge" TB_ARCH_STRING ".sh" andre@607: # endif andre@1127: #else // Not tag build means develpment build andre@1088: # define LIST_RESOURCE "/zertifikatsliste.txt" andre@607: # ifdef Q_OS_WIN andre@1127: # define SW_RESOURCE_VERSION "/development/TrustBridge-%1.exe" andre@1088: # define SW_RESOURCE "/development/TrustBridge.exe" andre@607: # else andre@1127: # define SW_RESOURCE_VERSION "/development/TrustBridge-%1" TB_ARCH_STRING ".sh" andre@1088: # define SW_RESOURCE "/development/TrustBridge" TB_ARCH_STRING ".sh" andre@607: # endif rrenkert@486: #endif aheinecke@7: andre@940: /* Help installation path the path relative to the installation directory where andre@940: * the help is placed.*/ andre@940: #ifdef WIN32 andre@940: #define HELP_PATH "/doc/index.html" andre@940: #else andre@940: #define HELP_PATH "/../share/doc/trustbridge/index.html" andre@940: #endif andre@940: andre@1109: static void activateDetailsButton(QPushButton *); andre@1109: static void deactivateDetailsButton(QPushButton *); andre@1109: andre@1339: /** @brief get the interval in milliseconds until the next update try. andre@1339: * andre@1339: * @param [in] tries the number of unsuccessful tries already done. andre@1339: * andre@1339: * @returns a positive value for the time in ms that should be waited. andre@1339: * -1 in case the software should abort. */ andre@1339: static int getNextUpdateInterval(int tries) andre@1339: { andre@1339: if (tries < 2) { andre@1339: // 5-10 minutes for the first two checks andre@1339: return ((5 + (qrand() % 5)) * 60 * 1000); andre@1339: } andre@1339: if (tries < 4) { andre@1339: // 15 minutes andre@1339: return 15 * 60 * 1000; andre@1339: } andre@1339: if (tries < 6) { andre@1339: // 30 minutes andre@1339: return 30 * 60 * 1000; andre@1339: } andre@1339: if (tries < 7) { andre@1339: return 60; andre@1339: } andre@1339: return -1; andre@1339: } andre@1339: aheinecke@365: MainWindow::MainWindow(bool trayMode): andre@1109: mTrayMode(trayMode), andre@1339: mManualDetailsShown(false), andre@1339: mFailedConnections(0) aheinecke@365: { aheinecke@0: createActions(); aheinecke@0: createTrayIcon(); andre@739: setupGUI(); andre@790: resize(900, 600); emanuel@709: setMinimumWidth(760); aheinecke@45: qRegisterMetaType("SSLConnection::ErrorCode"); rrenkert@265: qRegisterMetaType("Certificate::Status"); aheinecke@0: aheinecke@0: connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), aheinecke@0: this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); aheinecke@19: aheinecke@19: mMessageTimer = new QTimer(this); aheinecke@19: connect(mMessageTimer, SIGNAL(timeout()), this, SLOT(showMessage())); aheinecke@19: mMessageTimer->setInterval(NAG_INTERVAL_MINUTES * 60 * 1000); aheinecke@19: mMessageTimer->start(); andre@1339: checkUpdates(); andre@389: loadUnselectedCertificates(); andre@389: loadCertificateList(); andre@1125: andre@1125: if (mSettings.value("ShowOnNextStart").toBool()) { andre@1125: mSettings.remove("ShowOnNextStart"); andre@1125: show(); andre@1125: } else if (!trayMode) { aheinecke@365: show(); aheinecke@365: } aheinecke@0: } aheinecke@0: aheinecke@0: void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) aheinecke@0: { aheinecke@0: switch (reason) { aheinecke@0: case QSystemTrayIcon::Trigger: aheinecke@0: case QSystemTrayIcon::MiddleClick: aheinecke@0: showMessage(); aheinecke@0: break; aheinecke@16: case QSystemTrayIcon::DoubleClick: aheinecke@365: show(); aheinecke@16: break; aheinecke@0: default: aheinecke@0: ; aheinecke@0: } aheinecke@0: } aheinecke@0: aheinecke@71: void MainWindow::messageClicked() aheinecke@71: { aheinecke@551: if (mCurState == NewSoftwareAvailable) { aheinecke@587: verifySWData(); aheinecke@587: QString swFileName = mSettings.value("Software/available").toString(); aheinecke@587: if (swFileName.isEmpty()) { aheinecke@587: checkUpdates(true); aheinecke@587: mCurState = DownloadingSW; aheinecke@587: return; aheinecke@587: } aheinecke@587: installNewSW(swFileName, aheinecke@587: mSettings.value("Software/availableDate").toDateTime()); andre@965: } else { andre@965: show(); aheinecke@551: } aheinecke@71: } aheinecke@71: aheinecke@0: void MainWindow::showMessage() aheinecke@0: { aheinecke@591: if (mCurMessage.isEmpty()) { aheinecke@591: return; aheinecke@591: } andre@961: andre@961: if (!mTrayIcon->isVisible() && !mTrayIcon->isAlternative()) { andre@961: mTrayIcon->show(); andre@961: /* When the message is shown before the tray icon is fully visble andre@961: * and becoming visible may be delayed on some desktop environments andre@961: * the message will pop up somehere on the screen and not over andre@961: * the trayicon. So we delay here.*/ andre@961: QTimer::singleShot(2000, this, SLOT(showMessage())); andre@961: } else if (mCurState == NewSoftwareAvailable || !isVisible()) { andre@1144: if (mCurState == NewListAvailable) { andre@1144: mTrayIcon->showMessage(QApplication::applicationName(), mCurMessage, andre@1144: QSystemTrayIcon::Information, 10000, andre@1144: tr("Show recommendations")); andre@1144: } else if (mCurState == NewSoftwareAvailable || !mTrayIcon->isAlternative()) { andre@1144: /* Only show new list or new software in alternative as andre@1144: * the current tray icon alternative is too invasive for pure andre@1144: * informational messages. */ andre@1144: mTrayIcon->showMessage(QApplication::applicationName(), mCurMessage, andre@1144: QSystemTrayIcon::Information, 10000); andre@1144: } aheinecke@19: mMessageTimer->start(); // Restart the timer so that we don't spam aheinecke@16: } aheinecke@0: } aheinecke@16: aheinecke@587: void MainWindow::verifyListData() aheinecke@0: { andre@389: QString availableFileName = mSettings.value("List/available").toString(); andre@389: QString installedFileName = mSettings.value("List/installed").toString(); andre@389: if (!availableFileName.isEmpty()) { andre@1153: mListToInstall.readList(availableFileName.toUtf8().constData()); aheinecke@71: if (!mListToInstall.isValid()) { andre@1227: handleLTE(lteInvalidList); aheinecke@82: mCurState = TransferError; andre@389: QFile::remove(availableFileName); aheinecke@16: mSettings.remove("List/available"); aheinecke@16: mSettings.remove("List/availableDate"); andre@1227: } else { andre@1227: handleLTE(lteInvalidList, true); aheinecke@16: } aheinecke@16: } else { aheinecke@16: // Make sure the available notation is also removed aheinecke@16: mSettings.remove("List/available"); aheinecke@16: mSettings.remove("List/availableDate"); aheinecke@16: } aheinecke@16: andre@389: if (!installedFileName.isEmpty()) { andre@1153: mInstalledList.readList(installedFileName.toUtf8().constData()); andre@389: if (!mInstalledList.isValid()) { andre@389: // Probably a bug when Qt fileName is encoded and cFileName andre@389: // fails because of this. This needs a unit test! andre@389: // Maybe check that the file is in our data directory andre@389: QFile::remove(installedFileName); andre@389: mSettings.remove("List/installed"); andre@389: mSettings.remove("List/installedDate"); andre@389: } andre@389: } else { andre@389: mSettings.remove("List/installed"); andre@389: mSettings.remove("List/installedDate"); andre@389: } aheinecke@587: } andre@389: aheinecke@587: void MainWindow::verifySWData() aheinecke@587: { aheinecke@587: QString swFileName = mSettings.value("Software/available").toString(); andre@389: aheinecke@591: if (swFileName.isEmpty()) { andre@1127: qDebug() << "Date set but no fileName"; aheinecke@591: mSettings.remove("Software/availableDate"); aheinecke@591: return; aheinecke@591: } aheinecke@591: aheinecke@591: QFileInfo fi(swFileName); aheinecke@591: if (!fi.exists()) { aheinecke@16: mSettings.remove("Software/available"); aheinecke@16: mSettings.remove("Software/availableDate"); aheinecke@591: qDebug() << "Software does not yet exist."; aheinecke@591: return; aheinecke@591: } aheinecke@591: if (!fi.isExecutable()) { aheinecke@591: qWarning() << "Downloaded file: " << swFileName << " is not executable."; aheinecke@591: QFile::remove(swFileName); aheinecke@591: mSettings.remove("Software/available"); aheinecke@591: mSettings.remove("Software/availableDate"); aheinecke@591: return; aheinecke@591: } aheinecke@591: bin_verify_result verifyResult = verify_binary(swFileName.toUtf8().constData(), aheinecke@591: swFileName.toUtf8().size()); andre@1081: qDebug() << "Binary verify result: " << verifyResult.result; andre@1081: if (verifyResult.result != VerifyValid) { andre@1227: handleLTE(lteInvalidSoftware); aheinecke@591: qDebug() << "Failed to verify downloaded data."; aheinecke@591: QFile::remove(swFileName); aheinecke@591: mSettings.remove("Software/available"); aheinecke@591: mSettings.remove("Software/availableDate"); aheinecke@591: return; aheinecke@16: } andre@1227: handleLTE(lteInvalidSoftware, true); /* Reset error state */ andre@1081: fclose(verifyResult.fptr); aheinecke@16: } aheinecke@16: aheinecke@16: void MainWindow::handleNewList(const QString& fileName, const QDateTime& modDate) { andre@1232: andre@1232: if (mSettings.value("List/available").toString() == fileName && andre@1232: getState() == NewListAvailable) { andre@1232: qDebug() << "List already handled"; andre@1232: return; andre@1232: } else { andre@1232: qDebug() << "Handling list"; andre@1232: } aheinecke@71: mSettings.setValue("List/available", fileName); aheinecke@71: mSettings.setValue("List/availableDate", modDate); aheinecke@16: aheinecke@587: verifyListData(); andre@1362: if (!mListToInstall.isValid() || mListToInstall.date() <= mInstalledList.date()) { andre@1362: if ( mListToInstall.date() <= mInstalledList.date()) { andre@1362: qDebug() << "Newest list on the server is older then the installed list. "; andre@1362: qDebug() << "Installed: " << mInstalledList.date(); andre@1362: qDebug() << "Available: " << mListToInstall.date(); andre@1362: } else { andre@1362: qDebug() << "Failed to verify list."; andre@1362: } andre@1227: handleLTE(lteInvalidList); aheinecke@71: /* Downloader provided invalid files */ aheinecke@71: aheinecke@71: /* Retry the download again in 10 - 20 minutes */ aheinecke@71: QTimer::singleShot(600000 + (qrand() % 60000), this, SLOT(checkUpdates())); aheinecke@82: } else { andre@1154: if (mTrayIcon->isAlternative()) { andre@1154: mCurMessage = tr("An updated certificate list is available."); andre@1154: } else { andre@1154: mCurMessage = tr("An updated certificate list is available.") +" " + tr("Click here to install."); andre@1154: } aheinecke@82: setState(NewListAvailable); aheinecke@82: showMessage(); rrenkert@189: loadCertificateList(); aheinecke@71: } aheinecke@16: } aheinecke@16: aheinecke@16: void MainWindow::handleNewSW(const QString& fileName, const QDateTime& modDate) { andre@871: if (mTrayIcon->isAlternative()) { andre@871: mCurMessage = tr("An update for %1 is available.").arg( aheinecke@16: QApplication::applicationName()); andre@871: } else { andre@904: mCurMessage = QString(tr("An update for %1 is available.") + "\n" + andre@871: tr("Click here to download and install the update.")).arg( andre@871: QApplication::applicationName()); andre@871: } aheinecke@16: setState(NewSoftwareAvailable); aheinecke@16: mSettings.setValue("Software/available", fileName); aheinecke@16: mSettings.setValue("Software/availableDate", modDate); aheinecke@16: aheinecke@16: mSettings.sync(); aheinecke@16: showMessage(); aheinecke@16: } aheinecke@16: andre@1227: QString MainWindow::getPrettyInstallerName(QString realFileName) { andre@1116: QTemporaryDir tDir; andre@1116: if (!tDir.isValid()) { andre@1116: qDebug () << "Failed to create temporary directory."; andre@1227: showErrorMessage (tr("Failed to create temporary directory.") + "\n" + andre@1227: tr("Please ensure that you have the access rights to write in " andre@1227: "the temporary directory and that there is at least 20MB free " andre@1227: "disk space available.")); andre@1116: return QString(); andre@1116: } andre@1126: QString targetPath = tDir.path() + "/" + QObject::tr("TrustBridge-Updater", andre@1116: "Used as filename for the updater. Only use ASCII please."); andre@1116: andre@1116: tDir.setAutoRemove(false); andre@1116: #ifdef WIN32 andre@1116: targetPath += ".exe"; andre@1116: #endif andre@1116: if (!QFile::copy(realFileName, targetPath)) { andre@1227: showErrorMessage (tr("Failed to create a temporary copy of the installer.") + "\n" + andre@1227: tr("Please ensure that you have the access rights to write in " andre@1227: "the temporary directory and that there is at least 20MB free " andre@1227: "disk space available.")); andre@1116: qDebug() << "Failed to create temporary copy of installer."; andre@1116: } andre@1116: return targetPath; andre@1116: } andre@1116: andre@1252: void MainWindow::updaterFinished(int exitCode, QProcess::ExitStatus status) andre@1252: { andre@1252: if (status != QProcess::NormalExit) { andre@1252: syslog_error_printf("Update failed.\n"); andre@1252: qDebug() << "Failed to install update."; andre@1252: return; andre@1252: } andre@1252: if (exitCode != 0) { andre@1252: qDebug() << "Update not installed with error: " << exitCode; andre@1252: return; andre@1252: } andre@1252: qDebug() << "Restarting"; andre@1252: ProcessHelp::cleanUp(); andre@1252: QProcess::startDetached(qApp->applicationFilePath()); andre@1252: qApp->quit(); andre@1252: } andre@1252: aheinecke@551: void MainWindow::installNewSW(const QString& fileName, const QDateTime& modDate) { aheinecke@551: QFileInfo instProcInfo = QFileInfo(fileName); aheinecke@563: QString filePath = QDir::toNativeSeparators(instProcInfo.absoluteFilePath()); andre@1116: andre@1292: /* Reset available information. */ andre@1292: mSettings.setValue("Software/available", fileName); andre@1292: mSettings.setValue("Software/availableDate", modDate); andre@1292: andre@1116: /* Copy the file to a temporary name for installation */ andre@1116: filePath = getPrettyInstallerName(filePath); andre@1116: andre@1116: if (filePath.isEmpty()) { andre@1116: qDebug() << "Failed to copy updater to temporary location."; andre@1116: return; andre@1116: } andre@1116: mSettings.setValue("Software/Updater", filePath); /* So it can be deleted andre@1116: on next start */ andre@1116: mSettings.sync(); andre@1116: andre@1081: bin_verify_result vres = verify_binary(filePath.toUtf8().constData(), andre@1081: filePath.toUtf8().size()); aheinecke@592: andre@1081: if (vres.result != VerifyValid) { andre@1227: handleLTE(lteInvalidSoftware); aheinecke@592: qDebug() << "Invalid software. Not installing"; aheinecke@551: return; andre@1227: } andre@1227: handleLTE(lteInvalidSoftware, true); aheinecke@592: QFileInfo fi(QCoreApplication::applicationFilePath()); aheinecke@592: QDir installDir = fi.absoluteDir(); aheinecke@592: aheinecke@551: #ifdef WIN32 aheinecke@594: QString parameters = QString::fromLatin1("/S /UPDATE=1 /D=") + aheinecke@594: installDir.path().replace("/", "\\") + ""; aheinecke@594: aheinecke@551: SHELLEXECUTEINFOW shExecInfo; aheinecke@563: memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOW)); aheinecke@563: shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); aheinecke@563: aheinecke@563: shExecInfo.lpFile = reinterpret_cast (filePath.utf16()); aheinecke@594: shExecInfo.lpParameters = reinterpret_cast (parameters.utf16()); aheinecke@563: aheinecke@594: // shExecInfo.fMask = SEE_MASK_NOASYNC; aheinecke@594: shExecInfo.nShow = SW_SHOWDEFAULT; aheinecke@551: andre@1003: if (!is_system_install()) { aheinecke@551: shExecInfo.lpVerb = L"open"; aheinecke@551: } else { aheinecke@551: shExecInfo.lpVerb = L"runas"; aheinecke@551: } aheinecke@551: aheinecke@594: qDebug() << "Starting process: " << filePath aheinecke@594: << " with arguments: " << parameters; aheinecke@551: aheinecke@551: if (!ShellExecuteExW(&shExecInfo)) { aheinecke@551: /* Execution failed, maybe the user aborted the UAC check? */ aheinecke@551: char* errmsg = getLastErrorMsg(); aheinecke@551: QString qerrmsg = QString::fromUtf8(errmsg); aheinecke@551: free(errmsg); aheinecke@551: qDebug() << "Failed to start process: " << qerrmsg; aheinecke@551: setState(NewSoftwareAvailable); andre@1081: fclose(vres.fptr); aheinecke@551: return; aheinecke@551: } aheinecke@551: #else /* WIN32 */ andre@894: /* On linux installDir is /bin */ aheinecke@594: QStringList parameters; andre@894: installDir.cdUp(); andre@810: parameters << "--prefix" << installDir.path(); andre@1067: parameters << "--update"; andre@810: bool sudo_started = false; andre@841: bool use_sudo = is_admin() && is_system_install(); andre@1252: andre@1252: QProcess *updaterProcess = new QProcess(); andre@1252: andre@810: if (use_sudo) { andre@810: QStringList sudoPrograms; andre@987: sudoPrograms << "pkexec" << "kdesudo" << "sudo"; andre@810: QStringList sudoParams; andre@1252: sudoParams << filePath; andre@1252: sudoParams << parameters; andre@1252: updaterProcess->setArguments(sudoParams); aheinecke@551: andre@1260: #if 0 andre@1260: updaterProcess->setStandardErrorFile("/tmp/tb-inst-err.log"); andre@1260: updaterProcess->setStandardOutputFile("/tmp/tb-inst-out.log"); andre@1260: #endif andre@1260: andre@810: foreach (const QString &sProg, sudoPrograms) { andre@810: qDebug() << "Starting process " << sProg <<" params: " << sudoParams; andre@1252: updaterProcess->setProgram(sProg); andre@1252: updaterProcess->start(); andre@1252: if (!updaterProcess->waitForStarted() || andre@1252: updaterProcess->state() == QProcess::NotRunning) { andre@810: continue; andre@810: } else { andre@810: sudo_started = true; andre@1252: connect(updaterProcess, SIGNAL(finished(int, QProcess::ExitStatus)), andre@1252: this, SLOT(updaterFinished(int, QProcess::ExitStatus))); andre@810: break; andre@810: } andre@810: } andre@810: } andre@1252: if (!sudo_started) { andre@1252: qDebug() << "Starting process " << filePath <<" params: " << parameters; andre@1252: updaterProcess->setArguments(parameters); andre@1252: updaterProcess->setProgram(filePath); andre@1252: updaterProcess->start(); andre@1252: andre@1252: if (!updaterProcess->waitForStarted() || andre@1252: updaterProcess->state() == QProcess::NotRunning) { andre@1252: qDebug() << "Failed to start process."; andre@1252: } andre@1252: connect(updaterProcess, SIGNAL(finished(int, QProcess::ExitStatus)), andre@1272: this, SLOT(updaterFinished(int, QProcess::ExitStatus))); aheinecke@551: return; aheinecke@551: } andre@810: aheinecke@551: #endif andre@1125: if (isVisible()) { andre@1125: mSettings.setValue("ShowOnNextStart", true); andre@1125: mSettings.sync(); andre@1125: } andre@1081: andre@1081: syslog_info_printf ("Installing update: %s\n", fileName.toUtf8().constData()); aheinecke@551: /* Installer process should now be running. We exit */ andre@1081: fclose(vres.fptr); andre@1252: #ifdef WIN32 aheinecke@551: closeApp(); andre@1252: #endif aheinecke@551: } aheinecke@551: rrenkert@461: void MainWindow::checkUpdates(bool downloadSW) aheinecke@16: { aheinecke@587: verifyListData(); aheinecke@16: andre@1116: /* Delete old temporary installers if they exist */ andre@1116: QString oldUpdater = mSettings.value("Software/Updater").toString(); andre@1116: andre@1116: if (!oldUpdater.isEmpty()) { andre@1116: qDebug() << "Removing old updater: " << oldUpdater; andre@1116: QFileInfo fiUpdater(oldUpdater); andre@1116: if (!QFile::remove(fiUpdater.absoluteFilePath())) { andre@1116: qDebug() << "Failed to remove file"; andre@1116: } else { andre@1116: if (!fiUpdater.absoluteDir().rmdir(fiUpdater.absoluteDir().absolutePath())) { andre@1116: qDebug() << "Failed to remove temporary directory."; andre@1116: } andre@1116: } andre@1116: mSettings.remove("Software/Updater"); andre@1116: } andre@1116: aheinecke@551: if (!mSettings.contains("Software/installedDate") || aheinecke@551: mSettings.value("Software/installedVersion").toString() != QApplication::applicationVersion()) { aheinecke@551: /* This should only happen on initial startup and after an update has aheinecke@551: * been installed */ aheinecke@551: getLastModForCurrentVersion(); rrenkert@486: return; rrenkert@486: } aheinecke@17: QDateTime listInstalledLastMod = mSettings.value("List/installedDate").toDateTime(); aheinecke@17: QDateTime swInstalledLastMod = mSettings.value("Software/installedDate").toDateTime(); andre@389: QString listResource = QString::fromLatin1(LIST_RESOURCE); andre@389: QString swResource = QString::fromLatin1(SW_RESOURCE); andre@389: andre@389: #ifndef RELEASE_BUILD andre@389: /* Use this for testing to specify another file name for updates */ andre@389: listResource = mSettings.value("List/resource", listResource).toString(); andre@389: swResource = mSettings.value("Software/resource", swResource).toString(); andre@389: #endif andre@389: aheinecke@72: Downloader* downloader = new Downloader(this, aheinecke@72: QString::fromLatin1(SERVER_URL), aheinecke@72: QByteArray(), aheinecke@551: swInstalledLastMod, aheinecke@72: listInstalledLastMod, aheinecke@437: swResource, rrenkert@461: listResource, rrenkert@461: downloadSW); aheinecke@17: aheinecke@16: connect(downloader, SIGNAL(newListAvailable(const QString&, const QDateTime&)), aheinecke@16: this, SLOT(handleNewList(const QString&, const QDateTime&))); rrenkert@461: if (!downloadSW) { rrenkert@461: connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), aheinecke@551: this, SLOT(handleNewSW(const QString&, const QDateTime&))); aheinecke@551: } else { andre@708: setState(DownloadingSW); aheinecke@551: connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), aheinecke@551: this, SLOT(installNewSW(const QString&, const QDateTime&))); rrenkert@461: } aheinecke@551: aheinecke@16: connect(downloader, SIGNAL(finished()), downloader, SLOT(deleteLater())); andre@708: connect(downloader, SIGNAL(finished()), this, SLOT(updateCheckSuccess())); aheinecke@45: connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)), aheinecke@45: this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode))); aheinecke@10: downloader->start(); andre@1126: andre@1126: if (downloadSW && getState() == DownloadingSW && isVisible()) { andre@1126: QProgressDialog *progDlg = new QProgressDialog(this); andre@1126: progDlg->setCancelButton(0); andre@1126: progDlg->setRange(0,0); andre@1126: progDlg->setMinimumDuration(0); andre@1126: progDlg->setLabelText(tr("Downloading update...")); andre@1126: progDlg->show(); andre@1126: connect(downloader, SIGNAL(finished()), progDlg, SLOT(deleteLater())); andre@1126: connect(downloader, SIGNAL(finished()), progDlg, SLOT(cancel())); andre@1126: } aheinecke@0: } aheinecke@0: aheinecke@551: void MainWindow::getLastModForCurrentVersion() rrenkert@486: { rrenkert@486: QString softwareVersion = QString::fromLatin1(SW_RESOURCE_VERSION).arg( rrenkert@486: QApplication::applicationVersion()); andre@1292: qDebug() << "Getting last modified date for: " << softwareVersion; rrenkert@486: QString listResource = QString::fromLatin1(LIST_RESOURCE); rrenkert@486: Downloader* downloader = new Downloader(this, rrenkert@486: QString::fromLatin1(SERVER_URL), rrenkert@486: QByteArray(), rrenkert@486: QDateTime::currentDateTime(), rrenkert@486: QDateTime::currentDateTime(), rrenkert@486: softwareVersion, rrenkert@486: listResource, rrenkert@486: false); rrenkert@486: connect(downloader, SIGNAL(finished()), downloader, SLOT(deleteLater())); rrenkert@486: connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)), rrenkert@486: this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode))); rrenkert@486: connect(downloader, SIGNAL(lastModifiedDate(const QDateTime&)), aheinecke@551: this, SLOT(setLastModifiedSWDate(const QDateTime&))); andre@1155: connect(this, SIGNAL(destroyed(QObject*)), downloader, SLOT(quit())); rrenkert@486: downloader->start(); rrenkert@486: } rrenkert@486: aheinecke@551: void MainWindow::setLastModifiedSWDate(const QDateTime &date) rrenkert@486: { andre@1259: QDateTime swAvailableLastMod = mSettings.value("Software/availableDate").toDateTime(); andre@1259: andre@1259: if (swAvailableLastMod.isValid() && date.isValid()) { andre@1259: if (date >= swAvailableLastMod) { andre@1259: qDebug() << "Installed an update: " << date << andre@1259: " available was " << swAvailableLastMod; andre@1259: syslog_info_printf ("Software has been updated to version: %s\n", andre@1259: QApplication::applicationVersion().toUtf8().constData()); andre@1259: QString fileName = mSettings.value("Software/available").toString(); andre@1259: if (fileName.isEmpty()) { andre@1259: qDebug() << "Software marked as available but no filename set."; andre@1259: } else { andre@1259: if (QFile::remove(fileName)) { andre@1259: qDebug() << "Removed: " << fileName; andre@1259: } else { andre@1259: qDebug() << "Failed to remove: " << fileName; andre@1259: } andre@1259: } andre@1259: /* Clear out available data. */ andre@1259: mSettings.remove("Software/available"); andre@1259: mSettings.remove("Software/availableDate"); andre@1259: } andre@1259: } andre@1259: rrenkert@486: mSettings.beginGroup("Software"); andre@1145: #ifdef IS_TAG_BUILD andre@1145: /* We accept an invalid date to force installing any avialable update andre@1145: * in release mode. Otherwise we default to current datetime when we andre@1145: * did not find out version.*/ rrenkert@486: mSettings.setValue("installedDate", date); andre@1145: #else andre@1145: mSettings.setValue("installedDate", date.isValid() ? date : QDateTime::currentDateTime()); andre@1145: #endif aheinecke@551: mSettings.setValue("installedVersion", QApplication::applicationVersion()); rrenkert@486: mSettings.endGroup(); rrenkert@486: checkUpdates(); rrenkert@486: } rrenkert@486: aheinecke@45: void MainWindow::downloaderError(const QString &message, SSLConnection::ErrorCode error) aheinecke@16: { andre@708: syslog_error_printf ("Failed to check for updates: %s", message.toUtf8().constData()); andre@1230: qDebug() << "Downloader error: " << error; andre@1227: if (error == SSLConnection::InvalidCertificate) { andre@1227: handleLTE(lteInvalidCertificate); andre@1227: } else { andre@1227: handleLTE(lteNoConnection); andre@1227: } andre@1339: setState(TransferError); andre@1145: #ifdef IS_TAG_BUILD andre@1145: /* During tag build it should never happen that an url checked is not available andre@1145: * during development this is normal as each revision produces a new url. */ andre@1061: if (!isVisible()) { andre@1061: mCurMessage = message; andre@1061: mTrayIcon->show(); andre@1061: showMessage(); andre@1061: } else { andre@1061: showErrorMessage(tr("Failed to check for updates:") + "\n" + message); andre@1061: } andre@1127: #endif aheinecke@16: } aheinecke@16: aheinecke@0: void MainWindow::createActions() aheinecke@0: { aheinecke@0: mCheckUpdates = new QAction(tr("Check for Updates"), this); aheinecke@16: connect(mCheckUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates())); aheinecke@2: mQuitAction = new QAction(tr("Quit"), this); aheinecke@2: connect(mQuitAction, SIGNAL(triggered()), qApp, SLOT(quit())); aheinecke@0: } aheinecke@0: aheinecke@0: void MainWindow::createTrayIcon() aheinecke@0: { aheinecke@11: QIcon trayImg(":/img/tray_22.png"); andre@970: trayImg.addFile(":/img/tray_48.png", QSize(48,48)); aheinecke@0: aheinecke@0: mTrayMenu = new QMenu(this); aheinecke@0: mTrayMenu->addAction(mCheckUpdates); aheinecke@2: mTrayMenu->addAction(mQuitAction); aheinecke@0: andre@871: mTrayIcon = new TrayIcon(this); aheinecke@0: mTrayIcon->setContextMenu(mTrayMenu); aheinecke@0: aheinecke@0: mTrayIcon->setIcon(trayImg); andre@1326: andre@1326: if (mTrayIcon->isAlternative()) { andre@1326: /* On unity (the alternative notification usage) andre@1326: * we want to use the logo as window icon.*/ andre@1326: setWindowIcon(QIcon(":/img/logo.png")); andre@1326: } else { andre@1326: setWindowIcon(trayImg); andre@1326: } aheinecke@407: mTrayIcon->setToolTip(tr("TrustBridge")); aheinecke@71: aheinecke@71: connect(mTrayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked())); aheinecke@0: } rrenkert@155: andre@743: QWidget * MainWindow::createInfoWidget() andre@739: { andre@743: QWidget *theWidget = new QWidget; andre@739: QVBoxLayout *infoPanelLayout = new QVBoxLayout; andre@739: QHBoxLayout *infoHeaderLayout = new QHBoxLayout; andre@739: QVBoxLayout *infoHeaderTextLayout = new QVBoxLayout; andre@739: QVBoxLayout *infoCenterLayout = new QVBoxLayout; andre@739: andre@743: QString infoVersion = tr("Version: "); andre@739: infoVersion.append(QApplication::applicationVersion()); andre@739: QLabel *appVersion = new QLabel(infoVersion); andre@739: appVersion->setTextInteractionFlags( andre@739: Qt::TextSelectableByMouse | andre@739: Qt::TextSelectableByKeyboard); andre@739: andre@739: QFrame *infoHeaderSeparator = new QFrame(); andre@739: infoHeaderSeparator->setFrameShape(QFrame::HLine); andre@739: infoHeaderSeparator->setFrameShadow(QFrame::Sunken); andre@739: andre@739: infoHeaderTextLayout->addWidget(appVersion); andre@739: infoHeaderLayout->addLayout(infoHeaderTextLayout); andre@739: infoHeaderLayout->insertStretch(2, 10); andre@739: andre@743: QLabel *textDesc = new QLabel(tr("TrustBridge is a root certificate" andre@807: " installer for Windows and GNU/Linux.
") + andre@807: tr("The root certificate lists are managed" andre@739: " by the German " andre@807: "Federal Office for Information Security (BSI).

") + andre@807: tr("The software was developed by the companies" andre@739: " Intevation GmbH and " andre@739: " DN-Systems GmbH,
" emanuel@1215: " contracted by the BSI.

") + andre@807: tr("TrustBridge is Free Software licensed" emanuel@1215: " under GNU GPL v>=3.
Copyright (C) 2014 by Bundesamt für Sicherheit" emanuel@1215: " in der Informationstechnik

") + emanuel@1215: tr("TrustBridge uses several Free Software components with different licenses:") + emanuel@1215: "
  • TrustBridge source code (GPL v>=2)" + emanuel@1215: "
  • Qt (LGPL v==2.1)" + emanuel@1215: "
  • PolarSSL (GPL v>=2)" + emanuel@1215: "
  • Oxygen-Icons (LGPL v==3)" + emanuel@1215: "
  • Mozilla NSS (Mozilla Public License v2)" + emanuel@1215: "
  • libcurl (The curl license)
" + emanuel@1275: tr("You will find the legally binding details in the 'licenses' directory " emanuel@1275: "where TrustBridge is installed
" emanuel@1275: "or in the corresponding revision of the " emanuel@1215: "TrustBridge code repository.")); andre@807: textDesc->setTextFormat(Qt::RichText); andre@807: textDesc->setTextInteractionFlags( andre@739: Qt::TextSelectableByMouse | andre@844: Qt::TextSelectableByKeyboard | andre@844: Qt::LinksAccessibleByMouse); andre@844: textDesc->setOpenExternalLinks(true); andre@739: andre@739: infoCenterLayout->addWidget(infoHeaderSeparator); andre@739: infoCenterLayout->addWidget(textDesc); andre@739: infoCenterLayout->insertSpacing(2, 10); andre@739: infoCenterLayout->insertSpacing(4, 10); andre@739: infoCenterLayout->insertSpacing(6, 10); andre@940: andre@940: QHBoxLayout *helpButtonLayout = new QHBoxLayout(); emanuel@996: QPushButton *helpButton = new QPushButton(" " + tr("Show Help")); andre@964: helpButton->setIcon(QIcon(":/img/show-help_16.png")); andre@940: connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); andre@940: helpButtonLayout->addWidget(helpButton); andre@956: #ifdef USE_CURL emanuel@996: QPushButton *proxySettingsButton = new QPushButton(" " + tr("Proxy settings")); andre@956: proxySettingsButton->setIcon(QIcon(":/img/preferences-network_16.png")); andre@956: connect(proxySettingsButton, SIGNAL(clicked()), this, SLOT(showProxySettings())); andre@956: helpButtonLayout->addWidget(proxySettingsButton); andre@956: #endif andre@940: helpButtonLayout->addStretch(); andre@940: infoCenterLayout->addLayout(helpButtonLayout); andre@940: andre@739: infoCenterLayout->insertStretch(8, 10); andre@739: andre@739: infoPanelLayout->addLayout(infoHeaderLayout); andre@739: infoPanelLayout->addLayout(infoCenterLayout); andre@743: theWidget->setLayout(infoPanelLayout); andre@743: andre@743: return theWidget; andre@743: } andre@743: andre@743: QWidget * MainWindow::createUpdatesWidget() andre@743: { andre@743: QWidget * theWidget = new QWidget; andre@743: QVBoxLayout *updatesMainLayout = new QVBoxLayout; andre@743: andre@743: /* The header */ andre@1096: QVBoxLayout *updatesHeaderLayout = new QVBoxLayout; andre@1096: andre@1098: QGridLayout *detailsLayout = new QGridLayout; andre@743: andre@1098: /* Header 1: Action buttons and summary*/ andre@743: mUpdatesHeader = andre@743: new QLabel("

" + tr("Certificates unchanged")+ "

"); andre@1098: updatesHeaderLayout->addWidget(mUpdatesHeader); andre@743: andre@743: QHBoxLayout *updatesHeaderActionButtonLayout = new QHBoxLayout; andre@743: mQuitButton = new QPushButton(" " + tr("Quit without saving")); andre@743: mQuitButton->setIcon(QIcon(":/img/application-exit.png")); andre@743: mQuitButton->setFixedHeight(30); andre@743: andre@743: mInstallButton = new QPushButton(" " + tr("Install certificates again")); andre@743: mInstallButton->setFixedHeight(30); andre@743: #ifdef Q_OS_WIN andre@1003: if (is_system_install()) { andre@743: QIcon uacShield = QApplication::style()->standardIcon(QStyle::SP_VistaShield); andre@743: mInstallButton->setIcon(uacShield); andre@1313: } else { andre@1313: mInstallButton->setIcon(QIcon(":/img/do-changes-16.png")); andre@743: } andre@743: #else bernhard@1211: mInstallButton->setIcon(QIcon(":/img/do-changes-16.png")); andre@743: #endif andre@743: connect(mQuitButton, SIGNAL(clicked()), this, SLOT(closeApp())); andre@743: connect(mInstallButton, SIGNAL(clicked()), this, SLOT(checkAndInstallCerts())); andre@743: andre@743: updatesHeaderActionButtonLayout->addWidget(mInstallButton); andre@743: updatesHeaderActionButtonLayout->addWidget(mQuitButton); andre@1096: updatesHeaderActionButtonLayout->addStretch(-1); andre@1098: andre@1096: updatesHeaderLayout->addLayout(updatesHeaderActionButtonLayout); andre@1096: updatesHeaderLayout->addSpacing(20); andre@1096: andre@1096: /* The splitter line */ andre@1096: QFrame *line = new QFrame(); andre@1096: line->setFrameShape(QFrame::HLine); andre@1096: line->setFrameShadow(QFrame::Sunken); andre@1096: updatesHeaderLayout->addWidget(line); andre@743: andre@1098: updatesMainLayout->addLayout(updatesHeaderLayout); andre@1103: andre@1103: /* Central Header Details and update button. Part of the scroll area */ andre@1103: QScrollArea *centralScrollArea = new QScrollArea; andre@1103: QVBoxLayout *updatesCenterLayout = new QVBoxLayout; andre@1098: mUpdatesDetailsHeader = new QLabel(QString()); andre@1098: andre@1098: QHBoxLayout *updateDateAndSearchButton = new QHBoxLayout; andre@1098: mCertListVersion = andre@1098: new QLabel(QString()); andre@1098: mCertListVersionContents = new QLabel(QString()); andre@1098: const QDateTime lastCheck = mSettings.value("lastUpdateCheck").toDateTime().toLocalTime(); emanuel@1104: mLastUpdateCheck = new QLabel(tr("Last update check:")); andre@1098: if (lastCheck.isValid()) { andre@1098: const QString lastUpdateCheck = QLocale::system().toString(lastCheck, DATETIME_FORMAT); andre@1098: mLastUpdateCheckContents = new QLabel(lastUpdateCheck); andre@1098: } else { andre@1098: mLastUpdateCheckContents = new QLabel(tr("No connection with the updateserver.")); andre@1098: } andre@1098: QPushButton *searchUpdates = new QPushButton(" " + tr("Update")); emanuel@1104: searchUpdates->setFixedHeight(22); andre@1098: searchUpdates->setToolTip(tr("Check for Updates")); andre@1098: searchUpdates->setStyleSheet("font-size: 10px;"); andre@1098: searchUpdates->setIcon(QIcon(":/img/update-list.png")); andre@1098: connect(searchUpdates, SIGNAL(clicked()), this, SLOT(checkUpdates())); andre@1098: updateDateAndSearchButton->addWidget(mLastUpdateCheckContents); andre@1098: updateDateAndSearchButton->addWidget(searchUpdates); andre@1098: andre@1098: mUpdatesTip = andre@1098: new QLabel(QString()); andre@1098: mUpdatesTip->setWordWrap(true); andre@1098: andre@1098: // addWidget(*Widget, row, column, rowspan, colspan) andre@1103: updatesCenterLayout->addWidget(mUpdatesDetailsHeader); andre@1098: detailsLayout->addWidget(mLastUpdateCheck, 0, 0, 1, 1); andre@1098: detailsLayout->addLayout(updateDateAndSearchButton, 0, 1, 1, 1); andre@1098: detailsLayout->addWidget(mCertListVersion, 1, 0, 1, 1); andre@1098: detailsLayout->addWidget(mCertListVersionContents, 1, 1, 1, 1); andre@1098: detailsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 2, 2, 1, 1); andre@1098: detailsLayout->setColumnStretch(2, 1); andre@1098: andre@1103: updatesCenterLayout->addLayout(detailsLayout); andre@1098: andre@1103: updatesCenterLayout->addItem(new QSpacerItem(100, 10)); andre@1103: updatesCenterLayout->addWidget(mUpdatesTip); andre@1098: andre@743: /* The central panels. */ andre@743: QHBoxLayout *updatesNewLayout = new QHBoxLayout; andre@743: QHBoxLayout *updatesRemoveLayout = new QHBoxLayout; andre@743: QHBoxLayout *updatesManualLayout = new QHBoxLayout; andre@743: mUpdatesNewCertificates = andre@743: new QLabel("

" + andre@743: tr("Install new trusted certificates (%1/%2)").arg(0).arg(0) + andre@743: "

"); andre@790: mUpdatesDetailsNew = new QPushButton(); emanuel@845: mUpdatesDetailsNew->setText(" " + tr("Details")); andre@790: mUpdatesDetailsNew->setToolTip(tr("Show details")); emanuel@845: mUpdatesDetailsNew->setStyleSheet("font-size: 10px;"); emanuel@845: mUpdatesDetailsNew->setFixedHeight(22); emanuel@845: mUpdatesDetailsNew->setIcon(QIcon(":/img/dialog-information_16px.png")); andre@743: connect(mUpdatesDetailsNew, andre@743: SIGNAL(clicked()), andre@743: this, andre@743: SLOT(toggleUpdatesNew())); andre@743: updatesNewLayout->addWidget(mUpdatesNewCertificates); andre@790: updatesNewLayout->addWidget(mUpdatesDetailsNew); andre@743: updatesNewLayout->addStretch(1); andre@743: mUpdatesNew = new CertificateListWidget(this); andre@743: connect(mUpdatesNew, SIGNAL(certListChanged(int)), andre@743: this, SLOT(listChanged(int))); andre@743: mUpdatesNew->hide(); andre@743: andre@743: mUpdatesRemoveCertificates = andre@743: new QLabel("

" + andre@743: tr("Remove revoked certificates (%1/%2)").arg(0).arg(0) + andre@743: "

"); andre@790: mUpdatesDetailsRemove = new QPushButton(); emanuel@845: mUpdatesDetailsRemove->setText(" " + tr("Details")); andre@790: mUpdatesDetailsRemove->setToolTip(tr("Show details")); emanuel@845: mUpdatesDetailsRemove->setStyleSheet("font-size: 10px;"); emanuel@845: mUpdatesDetailsRemove->setFixedHeight(22); emanuel@845: mUpdatesDetailsRemove->setIcon(QIcon(":/img/dialog-information_16px.png")); andre@743: connect(mUpdatesDetailsRemove, andre@743: SIGNAL(clicked()), andre@743: this, andre@743: SLOT(toggleUpdatesRemove())); andre@743: updatesRemoveLayout->addWidget(mUpdatesRemoveCertificates); andre@790: updatesRemoveLayout->addWidget(mUpdatesDetailsRemove); andre@746: updatesRemoveLayout->addStretch(1); andre@743: mUpdatesRemove = new CertificateListWidget(this); andre@743: connect(mUpdatesRemove, SIGNAL(certListChanged(int)), andre@743: this, SLOT(listChanged(int))); andre@743: mUpdatesRemove->hide(); andre@743: andre@1098: mUpdatesManualCertificates = new QLabel(QString()); andre@790: mUpdatesDetailsManual = new QPushButton(); emanuel@845: mUpdatesDetailsManual->setText(" " + tr("Details")); andre@790: mUpdatesDetailsManual->setToolTip(tr("Show details")); emanuel@845: mUpdatesDetailsManual->setStyleSheet("font-size: 10px;"); emanuel@845: mUpdatesDetailsManual->setFixedHeight(22); emanuel@845: mUpdatesDetailsManual->setIcon(QIcon(":/img/dialog-information_16px.png")); andre@743: connect(mUpdatesDetailsManual, andre@743: SIGNAL(clicked()), andre@743: this, andre@743: SLOT(toggleUpdatesManual())); andre@743: mUpdatesDetailsManual->hide(); andre@743: updatesManualLayout->addWidget(mUpdatesManualCertificates); andre@790: updatesManualLayout->addWidget(mUpdatesDetailsManual); andre@747: updatesManualLayout->addStretch(1); andre@743: mUpdatesManual = new CertificateListWidget(this); andre@743: mUpdatesManual->hide(); andre@743: connect(mUpdatesManual, SIGNAL(certChanged(bool, const Certificate&)), andre@743: this, SLOT(removeFromManual(bool, const Certificate&))); andre@743: connect(mUpdatesManual, SIGNAL(certListChanged(int)), andre@743: this, SLOT(listChanged(int))); andre@743: andre@743: updatesNewLayout->setAlignment(Qt::AlignTop); andre@743: updatesRemoveLayout->setAlignment(Qt::AlignTop); andre@743: updatesManualLayout->setAlignment(Qt::AlignTop); andre@743: updatesCenterLayout->addLayout(updatesNewLayout); andre@743: updatesCenterLayout->addWidget(mUpdatesNew); andre@743: updatesCenterLayout->addLayout(updatesRemoveLayout); andre@743: updatesCenterLayout->addWidget(mUpdatesRemove); andre@1098: updatesCenterLayout->addSpacing(10); andre@743: updatesCenterLayout->addLayout(updatesManualLayout); andre@743: updatesCenterLayout->addWidget(mUpdatesManual); andre@743: andre@743: QWidget *dummyWidget = new QWidget; andre@743: dummyWidget->setLayout(updatesCenterLayout); andre@743: centralScrollArea->setWidgetResizable(true); andre@743: centralScrollArea->setWidget(dummyWidget); andre@743: centralScrollArea->setFrameShape(QFrame::NoFrame); andre@743: centralScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); andre@743: andre@743: updatesMainLayout->addWidget(centralScrollArea); andre@743: updatesCenterLayout->addSpacerItem(new QSpacerItem(0, 0, andre@743: QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); andre@743: theWidget->setLayout(updatesMainLayout); andre@743: return theWidget; andre@743: } andre@743: andre@743: andre@743: QWidget *MainWindow::createInstallWidget() andre@743: { andre@743: QWidget *theWidget = new QWidget; andre@743: QScrollArea *scrollArea = new QScrollArea; andre@743: QVBoxLayout *installPanelLayout = new QVBoxLayout; andre@743: QVBoxLayout *installHeaderLayout = new QVBoxLayout; andre@743: QVBoxLayout *installCenterLayout = new QVBoxLayout; andre@743: andre@743: QLabel *installHeaderLabel = andre@743: new QLabel("

" + tr("Trusted certificates") + "

"); andre@743: QLabel *installHeaderText = new QLabel(tr("The following list of trusted root" andre@743: " certificates is managed by the BSI. The BSI validates independently the" andre@743: " authenticity, security and actuality of these certificates.")); andre@743: installHeaderText->setWordWrap(true); andre@743: installHeaderLayout->addWidget(installHeaderLabel); andre@743: installHeaderLayout->addWidget(installHeaderText); andre@743: andre@743: QLabel *installCenterText = new QLabel(tr("Please choose the certificates" andre@743: " you want to trust or untrust. TrustBridge will install these certificates for your" andre@743: " secure communication for email and internet.")); andre@743: installCenterText->setWordWrap(true); andre@743: installCenterLayout->addWidget(installCenterText); andre@743: andre@743: installPanelLayout->addLayout(installHeaderLayout); andre@743: installPanelLayout->addLayout(installCenterLayout); andre@743: andre@743: mInstallList = new CertificateListWidget(this); andre@743: connect(mInstallList, SIGNAL(certChanged(bool, const Certificate&)), andre@743: this, SLOT(toggleInManual(bool, const Certificate&))); andre@743: andre@743: scrollArea->setWidgetResizable(true); andre@743: scrollArea->setWidget(mInstallList); andre@743: scrollArea->setFrameShape(QFrame::NoFrame); andre@743: andre@743: installPanelLayout->addWidget(scrollArea); andre@743: andre@743: theWidget->setLayout(installPanelLayout); andre@743: andre@743: return theWidget; andre@743: } andre@743: andre@743: QWidget *MainWindow::createRemoveWidget() andre@743: { andre@743: QWidget * theWidget = new QWidget; andre@743: QScrollArea *scrollArea = new QScrollArea; andre@743: QVBoxLayout *removePanelLayout = new QVBoxLayout; andre@743: QVBoxLayout *removeHeaderLayout = new QVBoxLayout; andre@743: QVBoxLayout *removeCenterLayout = new QVBoxLayout; andre@743: andre@743: QLabel *removeHeaderLabel = andre@743: new QLabel("

" + tr("Revoked certificates") + "

"); andre@743: QLabel *removeHeaderText = new QLabel(tr("Certificates can be corrupted" andre@743: " or stolen and misused in many ways. Therefore the BSI recommends" andre@743: " to remove all revoked certificates from your system.")); andre@743: removeHeaderText->setWordWrap(true); andre@743: removeHeaderLayout->addWidget(removeHeaderLabel); andre@743: removeHeaderLayout->addWidget(removeHeaderText); andre@743: bernhard@805: QLabel *removeCenterText = new QLabel(tr("The following unsecure certificates were" andre@743: " revoked by the BSI. Already uninstalled certificates cannot be reinstalled." andre@743: " It is recommended that you select all certificates to uninstall if you still" andre@743: " have revoked certificates installed.")); andre@743: removeCenterText->setWordWrap(true); andre@743: removeCenterLayout->addWidget(removeCenterText); andre@743: mRemoveList = new CertificateListWidget(this); andre@743: connect(mRemoveList, SIGNAL(certChanged(bool, const Certificate&)), andre@743: this, SLOT(toggleInManual(bool, const Certificate&))); andre@743: andre@743: removePanelLayout->addLayout(removeHeaderLayout); andre@743: removePanelLayout->addLayout(removeCenterLayout); andre@743: andre@743: scrollArea->setWidgetResizable(true); andre@743: scrollArea->setWidget(mRemoveList); andre@743: scrollArea->setFrameShape(QFrame::NoFrame); andre@743: removePanelLayout->addWidget(scrollArea); andre@743: theWidget->setLayout(removePanelLayout); andre@743: andre@743: return theWidget; andre@739: } andre@739: andre@739: void MainWindow::setupGUI() rrenkert@187: { rrenkert@187: // Create a central widget containing the main layout. rrenkert@187: QWidget *base = new QWidget; rrenkert@187: rrenkert@250: // Layouts and Container rrenkert@584: QVBoxLayout *mainLayout = new QVBoxLayout; rrenkert@205: QHBoxLayout *headerLayout = new QHBoxLayout; rrenkert@205: QVBoxLayout *headerTextLayout = new QVBoxLayout; andre@1094: QHBoxLayout *headerSubtitleLayout = new QHBoxLayout; rrenkert@584: QHBoxLayout *centerLayout = new QHBoxLayout; rrenkert@584: QVBoxLayout *buttonBarLayout = new QVBoxLayout; rrenkert@271: QHBoxLayout *bottomLayout = new QHBoxLayout; rrenkert@584: QHBoxLayout *containerLayout = new QHBoxLayout; rrenkert@187: rrenkert@584: // The header (icon, about text) rrenkert@584: QImage *logoImage = new QImage(":/img/logo.png"); rrenkert@584: QLabel *logo = new QLabel; rrenkert@584: logo->setBackgroundRole(QPalette::Base); rrenkert@584: logo->setPixmap(QPixmap::fromImage(*logoImage)); rrenkert@584: QLabel *title = new QLabel("

" + QString::fromLatin1(APPNAME) + "

"); emanuel@1104: QLabel *subTitle = new QLabel(tr("Trust in your digital communication")); andre@1094: QLabel *swVersion = new QLabel(QString::fromLatin1("") + andre@1094: tr("Version") + " " + QApplication::applicationVersion() + andre@1095: QString::fromLatin1(" ")); andre@1094: andre@1094: swVersion->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); andre@1094: swVersion->setTextFormat(Qt::RichText); andre@1094: andre@1094: headerSubtitleLayout->addWidget(subTitle); andre@1094: headerSubtitleLayout->addStretch(-1); andre@1094: headerSubtitleLayout->addWidget(swVersion); andre@1094: rrenkert@584: headerTextLayout->addWidget(title); andre@1094: headerTextLayout->addLayout(headerSubtitleLayout); rrenkert@584: headerLayout->addWidget(logo); rrenkert@584: headerLayout->addLayout(headerTextLayout); rrenkert@584: headerLayout->setStretch(0, 0); rrenkert@584: headerLayout->setStretch(1, 10); rrenkert@584: rrenkert@652: /*********************************** rrenkert@652: * The Buttonbar on the left side. rrenkert@652: ***********************************/ rrenkert@584: mButtonGroup = new QButtonGroup; rrenkert@584: andre@722: TextOverlayButton *updatesButton = new TextOverlayButton; rrenkert@584: updatesButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); andre@932: updatesButton->setBackgroundIcon(":/img/red-circle.png"); bernhard@1202: updatesButton->setIcon(QIcon(":/img/pending-changes-overview-48.png")); emanuel@732: updatesButton->setIconSize(QSize(48, 48)); andre@1099: updatesButton->setText(tr("Pending\nchanges")); emanuel@663: updatesButton->setFixedWidth(120); emanuel@732: updatesButton->setFixedHeight(90); rrenkert@584: updatesButton->setCheckable(true); rrenkert@584: updatesButton->setChecked(true); rrenkert@584: andre@722: connect(this, SIGNAL(changesChanged(const QString&)), andre@722: updatesButton, SLOT(setOverlay(const QString&))); andre@722: rrenkert@584: QToolButton *allInstallButton = new QToolButton; rrenkert@584: allInstallButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); rrenkert@584: allInstallButton->setIcon(QIcon(":/img/document-encrypt.png")); emanuel@732: allInstallButton->setIconSize(QSize(48, 48)); emanuel@659: allInstallButton->setText(tr("Trusted\ncertificates")); emanuel@663: allInstallButton->setFixedWidth(120); emanuel@732: allInstallButton->setFixedHeight(90); rrenkert@584: allInstallButton->setCheckable(true); rrenkert@584: rrenkert@584: QToolButton *allRemoveButton = new QToolButton; rrenkert@584: allRemoveButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); emanuel@673: allRemoveButton->setIcon(QIcon(":/img/dialog-warning.png")); emanuel@732: allRemoveButton->setIconSize(QSize(48, 48)); emanuel@661: allRemoveButton->setText(tr("Revoked\ncertificates")); emanuel@663: allRemoveButton->setFixedWidth(120); emanuel@732: allRemoveButton->setFixedHeight(90); rrenkert@584: allRemoveButton->setCheckable(true); rrenkert@584: rrenkert@584: QToolButton *infoButton = new QToolButton; rrenkert@584: infoButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); rrenkert@584: infoButton->setIcon(QIcon(":/img/dialog-information.png")); emanuel@732: infoButton->setIconSize(QSize(48, 48)); emanuel@659: infoButton->setText(tr("Information\nand help")); emanuel@663: infoButton->setFixedWidth(120); emanuel@732: infoButton->setFixedHeight(90); rrenkert@584: infoButton->setCheckable(true); rrenkert@584: rrenkert@584: mButtonGroup->addButton(updatesButton); rrenkert@584: mButtonGroup->addButton(allInstallButton); rrenkert@584: mButtonGroup->addButton(allRemoveButton); rrenkert@584: mButtonGroup->addButton(infoButton); rrenkert@584: mButtonGroup->setId(updatesButton, 0); rrenkert@584: mButtonGroup->setId(allInstallButton, 1); rrenkert@584: mButtonGroup->setId(allRemoveButton, 2); rrenkert@584: mButtonGroup->setId(infoButton, 3); rrenkert@584: rrenkert@584: connect(mButtonGroup, SIGNAL(buttonClicked(int)), rrenkert@584: this, SLOT(togglePages(int))); rrenkert@584: buttonBarLayout->addWidget(updatesButton); rrenkert@584: buttonBarLayout->addWidget(allInstallButton); rrenkert@584: buttonBarLayout->addWidget(allRemoveButton); rrenkert@584: buttonBarLayout->insertStretch(3, 10); rrenkert@584: buttonBarLayout->addWidget(infoButton); rrenkert@584: andre@743: /* The main pages.*/ rrenkert@584: andre@743: /* The updates page. */ andre@743: mUpdatesWidget = createUpdatesWidget(); rrenkert@584: andre@743: /* Install (trusted certs) Page */ andre@743: mInstallWidget = createInstallWidget(); rrenkert@584: rrenkert@652: /********************************** andre@743: * Page for certificates to be removed. rrenkert@652: **********************************/ andre@743: mRemoveWidget = createRemoveWidget(); rrenkert@584: rrenkert@652: /********************************** andre@743: * The info page. rrenkert@652: **********************************/ andre@743: mInfoWidget = createInfoWidget(); rrenkert@584: rrenkert@652: /******************************** rrenkert@652: * The main layout for pages. rrenkert@652: ********************************/ andre@743: mInstallWidget->hide(); andre@743: mRemoveWidget->hide(); andre@743: mInfoWidget->hide(); andre@743: containerLayout->addWidget(mUpdatesWidget); andre@743: containerLayout->addWidget(mInstallWidget); andre@743: containerLayout->addWidget(mRemoveWidget); andre@743: containerLayout->addWidget(mInfoWidget); rrenkert@584: rrenkert@584: centerLayout->addLayout(buttonBarLayout); rrenkert@584: centerLayout->addLayout(containerLayout); rrenkert@584: rrenkert@584: mainLayout->addLayout(headerLayout); rrenkert@584: mainLayout->addLayout(centerLayout); rrenkert@584: mainLayout->addLayout(bottomLayout); rrenkert@187: base->setLayout(mainLayout); rrenkert@187: setCentralWidget(base); rrenkert@187: } rrenkert@187: rrenkert@628: void MainWindow::listChanged(int selected) rrenkert@628: { andre@716: Q_UNUSED (selected); andre@722: setChangeCount(mUpdatesRemove->selectedCertCount() + andre@1107: mUpdatesNew->selectedCertCount() + mUpdatesManual->certificates().size()); andre@696: andre@718: /* Show a different tip in case of manual changes, updates aviailable, updates and manual andre@718: * changes available */ andre@1107: if (changeCount() && !mUpdatesManual->certificates().size()) { andre@718: mUpdatesTip->setText( emanuel@1104: tr("You should apply the following, recommended changes to your certificate stores:")); andre@718: } else { andre@1236: mUpdatesTip->setText(""); andre@718: } andre@718: andre@716: if (!changeCount()) { andre@693: /* No changes */ andre@702: mQuitButton->setText(" " + tr("Quit")); andre@696: mUpdatesHeader->setText("

" + tr("Certificates unchanged") + andre@696: "

"); andre@718: mInstallButton->setText(" " + tr("Install certificates again")); andre@693: } else { andre@702: mQuitButton->setText(" " + tr("Quit without saving")); emanuel@1104: mUpdatesHeader->setText("

" + tr("Pending changes (%1)") andre@716: .arg(changeCount()) + andre@696: "

"); andre@718: mInstallButton->setText(" " + tr("Apply changes")); andre@693: } andre@693: andre@719: if (mUpdatesManual->certificates().size()) { andre@719: mUpdatesDetailsManual->show(); andre@1109: if (mManualDetailsShown) { andre@1109: mUpdatesManual->show(); andre@1109: deactivateDetailsButton(mUpdatesDetailsManual); andre@1109: } else { andre@1109: activateDetailsButton(mUpdatesDetailsManual); andre@1109: } andre@719: } else { andre@719: mUpdatesDetailsManual->hide(); andre@719: mUpdatesManual->hide(); andre@719: } andre@1098: mUpdatesManualCertificates->setText("

" + andre@1109: tr("Manual changes (%1)").arg(mUpdatesManual->certificates().size()) + andre@1098: "

"); andre@719: andre@717: if (mUpdatesNew->certificates().size()) { andre@717: mUpdatesNewCertificates->setText("

" + andre@717: tr("Install new trusted certificates (%1/%2)") andre@717: .arg(mUpdatesNew->selectedCertCount()) andre@717: .arg(mUpdatesNew->certificates().size()) + andre@717: "

"); andre@1311: if (!mUpdatesNew->isVisible()) { andre@1311: activateDetailsButton(mUpdatesDetailsNew); andre@1311: } andre@717: mUpdatesNewCertificates->show(); andre@717: } else { andre@717: mUpdatesDetailsNew->hide(); andre@719: mUpdatesNew->hide(); andre@717: mUpdatesNewCertificates->hide(); andre@717: } andre@717: andre@717: if (mUpdatesRemove->certificates().size()) { andre@717: mUpdatesRemoveCertificates->setText("

" + andre@717: tr("Remove revoked certificates (%1/%2)") andre@717: .arg(mUpdatesRemove->selectedCertCount()) andre@717: .arg(mUpdatesRemove->certificates().size()) + andre@717: "

"); andre@717: mUpdatesRemoveCertificates->show(); andre@1311: if (!mUpdatesRemove->isVisible()) { andre@1311: activateDetailsButton(mUpdatesDetailsRemove); andre@1311: } andre@717: } else { andre@717: mUpdatesRemoveCertificates->hide(); andre@717: mUpdatesDetailsRemove->hide(); andre@719: mUpdatesRemove->hide(); andre@717: } andre@1098: andre@1098: /* Update the details header */ andre@1098: if (mUpdatesRemove->certificates().size() || andre@1098: mUpdatesNew->certificates().size()) { andre@1098: mUpdatesDetailsHeader->setText("

" + andre@1098: tr("New, recommended changes (%1/%2)") andre@1098: .arg(mUpdatesRemove->selectedCertCount() + andre@1098: mUpdatesNew->selectedCertCount()) andre@1098: .arg(mUpdatesRemove->certificates().size() + andre@1098: mUpdatesNew->certificates().size()) + andre@1098: "

"); andre@1098: } else { andre@1098: mUpdatesDetailsHeader->setText(QString::fromLatin1("

") + andre@1098: tr("No new recommendations") + QString::fromLatin1("

")); andre@1098: } andre@1098: andre@1098: if (mListToInstall.isValid()) { emanuel@1104: mCertListVersion->setText(tr("Certificate list from:")); andre@1098: mCertListVersionContents->setText(QLocale::system().toString( andre@1098: mListToInstall.date().toLocalTime(), DATETIME_FORMAT)); andre@1098: } else { andre@1098: if (mInstalledList.isValid()) { emanuel@1104: mCertListVersion->setText(tr("Currently installed certificate list:")); andre@1098: mCertListVersionContents->setText(QLocale::system().toString( andre@1098: mInstalledList.date().toLocalTime(), DATETIME_FORMAT)); andre@1098: } else { emanuel@1104: mCertListVersion->setText(tr("No certificate list installed.")); andre@1098: mCertListVersionContents->setText(""); andre@1098: } andre@1098: } rrenkert@628: } rrenkert@628: rrenkert@189: void MainWindow::loadCertificateList() rrenkert@189: { andre@1162: /* TODO (issue134): if nothing is available (neither old nor new) add some progress andre@389: * indication */ andre@1233: setUpdatesEnabled(false); rrenkert@628: mInstallList->clear(); andre@684: mRemoveList->clear(); rrenkert@628: mUpdatesNew->clear(); rrenkert@628: mUpdatesRemove->clear(); rrenkert@445: QList newInstallCerts; rrenkert@445: QList newRemoveCerts; rrenkert@445: QList oldInstallCerts; rrenkert@445: QList oldRemoveCerts; andre@389: rrenkert@445: if (mListToInstall.getCertificates().isEmpty()) { rrenkert@445: // No new list available, add old certificates. rrenkert@445: foreach (const Certificate &cert, mInstalledList.getCertificates()) { rrenkert@640: bool state = !mPreviouslyUnselected.contains(cert.base64Line()); rrenkert@445: if (cert.isInstallCert()) { rrenkert@445: oldInstallCerts.append(cert); andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1106: if (!state) { bernhard@1209: btnIcon.addFile(":/img/cert-to-be-installed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-not-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be installed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certifcate is not installed.")); andre@1106: } else { andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate is installed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certificate will be removed.")); andre@1148: btnIcon.addFile(":/img/cert-is-installed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1106: } andre@1106: actionBtn->setIcon(btnIcon); andre@1106: mInstallList->addCertificate(cert, state, actionBtn); rrenkert@267: } rrenkert@267: else { rrenkert@445: oldRemoveCerts.append(cert); andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be removed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certificate has not been removed.")); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-is-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1148: btnIcon.addFile(":/img/cert-not-installed-good-48.png", QSize(48, 48), QIcon::Disabled, QIcon::Off); andre@1106: actionBtn->setIcon(btnIcon); andre@1106: if (state) { andre@1106: actionBtn->setEnabled(false); andre@1110: actionBtn->setToolTip(tr("Certificate has been removed.")); andre@1106: } andre@1106: mRemoveList->addCertificate(cert, state, actionBtn); rrenkert@267: } rrenkert@267: } rrenkert@445: } rrenkert@445: else { rrenkert@445: // Sort and filter both lists. rrenkert@445: foreach (const Certificate &cert, mListToInstall.getCertificates()) { rrenkert@640: bool state = !mPreviouslyUnselected.contains(cert.base64Line()); rrenkert@445: if (cert.isInstallCert()) { rrenkert@445: // Certificate with status "install". rrenkert@445: if (mInstalledList.getCertificates().contains(cert)) { rrenkert@445: // Was in the old list. rrenkert@445: oldInstallCerts.append(cert); andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate is installed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certifcate is not installed.")); andre@1148: btnIcon.addFile(":/img/cert-is-installed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-not-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1106: actionBtn->setIcon(btnIcon); andre@1106: mInstallList->addCertificate(cert, state, actionBtn); rrenkert@445: } rrenkert@445: else { rrenkert@445: // Is a brand new certificate rrenkert@445: newInstallCerts.append(cert); andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be installed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certificate will not be installed.")); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-installed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-not-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1106: actionBtn->setIcon(btnIcon); andre@1106: mUpdatesNew->addCertificate(cert, state, actionBtn); rrenkert@445: } rrenkert@267: } rrenkert@267: else { rrenkert@445: // Certificate with status "remove". rrenkert@445: if (mInstalledList.getCertificates().contains(cert)) { rrenkert@445: // Was in the old list. rrenkert@445: oldRemoveCerts.append(cert); rrenkert@640: // Is removed, so set editable to false. andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be removed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certificate has not been removed.")); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-is-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1148: btnIcon.addFile(":/img/cert-not-installed-good-48.png", QSize(48, 48), QIcon::Disabled, QIcon::Off); andre@1106: actionBtn->setIcon(btnIcon); andre@1106: if (state) { andre@1106: actionBtn->setEnabled(false); andre@1110: actionBtn->setToolTip(tr("Certificate has been removed.")); andre@1106: } andre@1106: mRemoveList->addCertificate(cert, state, actionBtn); rrenkert@445: } rrenkert@445: else { rrenkert@445: // Was in the old list with status "install" and now has the rrenkert@445: // status "remove". rrenkert@445: newRemoveCerts.append(cert); andre@1234: CheckLessToolBtn* actionBtn = new CheckLessToolBtn(); andre@1106: QIcon btnIcon; andre@1110: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be removed.")); andre@1110: actionBtn->setProperty("ToolTip_On", tr("Certificate will not be removed.")); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1148: btnIcon.addFile(":/img/cert-is-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1106: actionBtn->setIcon(btnIcon); andre@1106: mUpdatesRemove->addCertificate(cert, state, actionBtn); rrenkert@445: } rrenkert@267: } rrenkert@267: } andre@691: } andre@692: andre@692: listChanged(0); andre@1233: setUpdatesEnabled(true); rrenkert@271: } aheinecke@365: aheinecke@365: void MainWindow::installerError(const QString& errMsg) { aheinecke@365: QMessageBox::warning(this, tr("Error executing update"), errMsg); aheinecke@365: } aheinecke@365: andre@389: void MainWindow::installerSuccess() { andre@997: if (mCurState == NewListAvailable ) { andre@389: mCurState = NothingChanged; andre@389: mCurMessage = QString(); andre@997: } andre@389: andre@997: QString listFileName = mSettings.value("List/available").toString(); andre@997: QDateTime listFileDate = mSettings.value("List/availableDate").toDateTime(); andre@997: if (!listFileName.isEmpty() && listFileDate.isValid()) { andre@389: mSettings.remove("List/available"); andre@389: mSettings.remove("List/availableDate"); andre@389: andre@857: /* Rename the installed list to list-installed.txt so that external andre@857: * programs (like the uninstaller can easily recognize it). */ andre@857: QString dataLoc = andre@857: QStandardPaths::writableLocation(QStandardPaths::DataLocation); andre@857: QDir dataDir(dataLoc); andre@857: if (!dataDir.exists()) { andre@857: /* Should not happen */ andre@857: qWarning() << "Data dir removed."; andre@857: return; andre@857: } andre@389: andre@857: QFileInfo oldList (dataDir.absoluteFilePath("list-installed.txt")); andre@857: if (oldList.exists()) { andre@857: qDebug() << "Removing old list: " << oldList.filePath(); andre@857: if (!QFile::remove(oldList.filePath())) { andre@857: qWarning() << "Removal of old list failed."; andre@857: return; andre@857: } andre@857: } andre@857: QFile newList(listFileName); andre@857: if (!newList.rename(oldList.filePath())) { andre@857: qWarning() << "Failed to rename new list."; andre@857: return; andre@857: } andre@857: andre@857: mSettings.setValue("List/installed", oldList.filePath()); andre@389: mSettings.setValue("List/installedDate", listFileDate); andre@857: mInstalledList = CertificateList(oldList.filePath().toUtf8().constData()); andre@857: if (!mInstalledList.isValid()) { andre@857: /* Something went wrong. Go back to square one. */ andre@857: qWarning () << "List corrupted after installation"; andre@857: mInstalledList = CertificateList(); andre@857: QFile::remove(oldList.filePath()); andre@857: mSettings.remove("List/installed"); andre@857: mSettings.remove("List/installedDate"); andre@857: } andre@389: } andre@997: mListToInstall = CertificateList(); andre@684: mUpdatesManual->clear(); rrenkert@628: loadCertificateList(); andre@389: } andre@389: aheinecke@365: void MainWindow::installCerts() { aheinecke@365: QStringList choices; rrenkert@640: QStringList unselected; aheinecke@365: rrenkert@640: choices << mUpdatesNew->selectedCertificates(); rrenkert@640: choices << mUpdatesRemove->selectedCertificates(); rrenkert@640: andre@1308: choices << mUpdatesManual->unselectedCertificates(); andre@669: andre@669: /* Also include the old certificates */ andre@669: choices << mInstallList->selectedCertificates(); andre@669: choices << mRemoveList->selectedCertificates(); andre@669: andre@1308: QStringList selectedManuals = mUpdatesManual->selectedCertificates(); andre@1308: for(int i = 0; i < selectedManuals.size(); i++) { andre@1308: if (selectedManuals.at(i).startsWith("I:")) { andre@1308: QString certLine = selectedManuals.at(i); rrenkert@640: certLine[0] = 'R'; rrenkert@640: choices << certLine; rrenkert@640: } rrenkert@640: } rrenkert@640: rrenkert@640: unselected << mUpdatesNew->unselectedCertificates(); rrenkert@640: unselected << mUpdatesRemove->unselectedCertificates(); rrenkert@640: unselected << mInstallList->unselectedCertificates(); rrenkert@640: unselected << mRemoveList->unselectedCertificates(); rrenkert@628: andre@918: #ifdef Q_OS_WIN andre@1003: if (!is_system_install()) { andre@1003: QMessageBox::warning(this, emanuel@926: tr("Installation with standard user account"), emanuel@1006: tr("Windows will now ask you to confirm each root certificate modification " emanuel@926: "because TrustBridge does not have the necessary privileges to install " emanuel@926: "root certificates into the Windows certificate store silently.")); andre@918: } andre@918: #endif andre@918: aheinecke@365: QProgressDialog *progress = new QProgressDialog(this); aheinecke@365: progress->setWindowModality(Qt::WindowModal); aheinecke@365: progress->setLabelText(tr("Installing certificates...")); aheinecke@365: progress->setCancelButton(0); aheinecke@365: progress->setRange(0,0); aheinecke@365: progress->setMinimumDuration(0); aheinecke@365: progress->show(); aheinecke@365: andre@1082: CertificateList *instList = mListToInstall.isValid() ? andre@1082: &mListToInstall : andre@1082: &mInstalledList; andre@1082: aheinecke@365: InstallWrapper *instWrap = new InstallWrapper(this, andre@1082: instList->fileName(), aheinecke@365: choices); andre@1082: andre@1082: syslog_info_printf ("Installing certificate list: '%s' Version '%s'\n", andre@1082: instList->fileName().toUtf8().constData(), andre@1082: instList->date().toString().toUtf8().constData()); aheinecke@365: /* Clean up object and progress dialog */ aheinecke@365: connect(instWrap, SIGNAL(finished()), instWrap, SLOT(deleteLater())); aheinecke@365: connect(instWrap, SIGNAL(finished()), progress, SLOT(deleteLater())); aheinecke@365: connect(instWrap, SIGNAL(finished()), progress, SLOT(cancel())); andre@389: connect(instWrap, SIGNAL(installationSuccessful()), andre@389: this, SLOT(installerSuccess())); aheinecke@365: connect(instWrap, SIGNAL(error(const QString &)), andre@389: this, SLOT(installerError(const QString &))); aheinecke@365: instWrap->start(); rrenkert@640: rrenkert@640: if (!saveUnselectedCertificates(unselected)) { andre@372: qWarning() << "Failed to save previosly unselected certificates."; andre@372: } rrenkert@640: aheinecke@365: } andre@372: andre@372: void MainWindow::loadUnselectedCertificates() andre@372: { andre@372: mPreviouslyUnselected.clear(); andre@372: mSettings.beginGroup("unselected"); andre@372: QStringList keys = mSettings.allKeys(); andre@372: foreach (const QString &key, keys) { andre@372: mPreviouslyUnselected << mSettings.value(key, QString()).toString(); andre@372: } andre@372: mSettings.endGroup(); andre@372: } andre@372: rrenkert@640: bool MainWindow::saveUnselectedCertificates(QStringList unselected) andre@372: { rrenkert@479: mPreviouslyUnselected.clear(); andre@372: mSettings.beginGroup("unselected"); andre@372: mSettings.remove(""); /* Clears old choices */ rrenkert@640: for (int i = 0; i < unselected.size(); i++) { rrenkert@640: QString key = QString::fromLatin1("cert%1").arg(i); rrenkert@640: QString value = unselected.at(i); rrenkert@640: mSettings.setValue(key, value); rrenkert@640: mPreviouslyUnselected << value; andre@372: } andre@372: mSettings.endGroup(); andre@372: mSettings.sync(); andre@372: return mSettings.status() == QSettings::NoError; andre@372: } rrenkert@447: rrenkert@640: void MainWindow::toggleInManual(bool state, const Certificate &cert) rrenkert@640: { rrenkert@640: if (!mUpdatesManual->contains(cert)) { andre@1106: QToolButton* actionBtn = new QToolButton(); andre@1106: QIcon btnIcon; andre@1201: if (mRemoveList->contains(cert)) { bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); andre@1201: btnIcon.addFile(":/img/cert-is-installed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1201: actionBtn->setProperty("ToolTip_On", tr("Certificate will be removed.")); andre@1201: /* Off should never be possible here andre@1201: * As the manual change of removed certificates is disabled */ andre@1201: actionBtn->setProperty("ToolTip_Off", tr("Certificate will not be removed.")); andre@1201: } else { bernhard@1209: btnIcon.addFile(":/img/cert-to-be-installed-good-48.png", QSize(48, 48), QIcon::Normal, QIcon::On); bernhard@1209: btnIcon.addFile(":/img/cert-to-be-removed-bad-48.png", QSize(48, 48), QIcon::Normal, QIcon::Off); andre@1201: actionBtn->setProperty("ToolTip_On", tr("Certificate will be installed.")); andre@1201: actionBtn->setProperty("ToolTip_Off", tr("Certificate will be removed.")); andre@1201: } andre@1106: actionBtn->setIcon(btnIcon); andre@1106: mUpdatesManual->addCertificate(cert, state, actionBtn); rrenkert@640: } rrenkert@640: else { andre@1107: mUpdatesManual->removeCertificate(cert); rrenkert@640: } rrenkert@640: } rrenkert@640: rrenkert@640: void MainWindow::removeFromManual(bool state, const Certificate &cert) rrenkert@640: { andre@1107: mUpdatesManual->removeCertificate(cert); rrenkert@640: rrenkert@640: if (cert.isInstallCert()) { rrenkert@640: mInstallList->setCertState(state, cert); rrenkert@640: } rrenkert@640: else { rrenkert@640: mRemoveList->setCertState(state, cert); rrenkert@640: } rrenkert@640: } rrenkert@640: rrenkert@447: void MainWindow::closeApp() rrenkert@447: { wilde@782: ProcessHelp::cleanUp(); rrenkert@447: qApp->quit(); rrenkert@447: } rrenkert@584: andre@609: void MainWindow::checkAndInstallCerts() andre@609: { andre@1246: #ifdef WIN32 andre@609: /* Checking before opening the dialog should be cheaper */ andre@609: QList pids = ProcessHelp::getProcessesIdForName("firefox"); andre@609: pids.append(ProcessHelp::getProcessesIdForName("thunderbird")); andre@609: andre@609: if (pids.isEmpty()) { andre@609: installCerts(); andre@609: return; andre@609: } andre@609: andre@609: QStringList pNames; andre@609: pNames << "firefox" << "thunderbird"; andre@609: andre@609: ProcessWaitDialog *waitDialog = new ProcessWaitDialog(this, pNames); andre@609: andre@609: connect(waitDialog, SIGNAL(accepted()), this, SLOT(installCerts())); andre@609: connect(waitDialog, SIGNAL(accepted()), waitDialog, SLOT(deleteLater())); andre@609: andre@609: waitDialog->exec(); andre@609: return; andre@1246: #else andre@1246: /* On GNU Linux finding all firefox / thunderbird instances which andre@1246: * could be found on the filesystem is more difficult. So andre@1246: * for now we just show a generic warning dialog. */ andre@1246: QMessageBox* warnMessage = new QMessageBox (QMessageBox::Information, andre@1246: tr("Firefox and Thunderbird certificate installation."), andre@1246: tr("Please close all running Firefox and Thunderbird instances " andre@1246: "before continuing with the installation."), QMessageBox::Ok | QMessageBox::No, andre@1246: this); andre@1246: warnMessage->button(QMessageBox::Ok)->setText(tr("Continue")); andre@1246: warnMessage->setDefaultButton(QMessageBox::Ok); andre@1246: warnMessage->button(QMessageBox::No)->setText(tr("Cancel")); andre@1246: warnMessage->setWindowIcon(windowIcon()); andre@1246: warnMessage->setIconPixmap(QIcon(":/img/dialog-warning.png").pixmap(QSize(48,48))); andre@1246: andre@1246: connect(warnMessage->button(QMessageBox::Ok), SIGNAL(clicked()), this, SLOT(installCerts())); andre@1246: warnMessage->exec(); andre@1246: return; andre@1246: #endif andre@609: } andre@654: rrenkert@584: void MainWindow::togglePages(int button) rrenkert@584: { andre@743: mUpdatesWidget->hide(); andre@743: mInstallWidget->hide(); andre@743: mRemoveWidget->hide(); andre@743: mInfoWidget->hide(); rrenkert@584: switch(button) { andre@743: case 0: mUpdatesWidget->show(); break; andre@743: case 1: mInstallWidget->show(); break; andre@743: case 2: mRemoveWidget->show(); break; andre@743: case 3: mInfoWidget->show(); break; andre@743: default: mUpdatesWidget->show(); break; rrenkert@584: } rrenkert@584: return; rrenkert@584: } rrenkert@584: andre@1098: static void deactivateDetailsButton(QPushButton *btn) { andre@1098: btn->setToolTip(QObject::tr("Hide details")); emanuel@1104: btn->setText(" " + QObject::tr("Less")); andre@1098: btn->setIcon(QIcon(":/img/dialog-information_grey_16px.png")); andre@1098: } andre@1098: andre@1098: static void activateDetailsButton(QPushButton *btn) { andre@1098: btn->setToolTip(QObject::tr("Show details")); emanuel@1104: btn->setText(" " + QObject::tr("Details")); andre@1098: btn->setIcon(QIcon(":/img/dialog-information_16px.png")); andre@1311: btn->show(); andre@1098: } andre@1098: rrenkert@584: void MainWindow::toggleUpdatesNew() { rrenkert@584: if (!mUpdatesNew->isVisible()) { rrenkert@584: mUpdatesNew->show(); andre@1098: deactivateDetailsButton(mUpdatesDetailsNew); rrenkert@584: } rrenkert@584: else { rrenkert@653: mUpdatesNew->hide(); andre@1098: activateDetailsButton(mUpdatesDetailsNew); rrenkert@584: } rrenkert@584: } rrenkert@584: rrenkert@584: void MainWindow::toggleUpdatesRemove() { rrenkert@584: if (!mUpdatesRemove->isVisible()) { rrenkert@584: mUpdatesRemove->show(); andre@1098: deactivateDetailsButton(mUpdatesDetailsRemove); rrenkert@584: } rrenkert@584: else { rrenkert@653: mUpdatesRemove->hide(); andre@1098: activateDetailsButton(mUpdatesDetailsRemove); rrenkert@584: } rrenkert@584: } rrenkert@584: rrenkert@584: void MainWindow::toggleUpdatesManual() { rrenkert@584: if (!mUpdatesManual->isVisible()) { rrenkert@584: mUpdatesManual->show(); andre@1109: mManualDetailsShown = true; andre@1098: deactivateDetailsButton(mUpdatesDetailsManual); rrenkert@584: } rrenkert@584: else { rrenkert@653: mUpdatesManual->hide(); andre@1109: mManualDetailsShown = false; andre@1098: activateDetailsButton(mUpdatesDetailsManual); rrenkert@584: } rrenkert@584: } andre@690: andre@690: void MainWindow::closeEvent(QCloseEvent *event) andre@690: { andre@690: if (getState() == NewListAvailable) { andre@690: /* Only minimize to tray if there is a new list */ andre@690: QMainWindow::closeEvent(event); andre@965: mTrayIcon->show(); andre@690: return; andre@690: } andre@690: return closeApp(); andre@690: } andre@708: andre@708: void MainWindow::updateCheckSuccess() andre@708: { andre@708: if (getState() != TransferError) { andre@708: const QDateTime now = QDateTime::currentDateTime(); andre@708: mSettings.setValue("lastUpdateCheck", now); andre@754: mLastUpdateCheckContents->setText(QLocale::system().toString(now, DATETIME_FORMAT)); andre@754: mLastUpdateCheckContents->show(); andre@708: mLastUpdateCheck->show(); andre@708: syslog_info_printf(tr("Sucessfully checked for updates.").toUtf8().constData()); andre@1227: handleLTE(lteNoConnection, true); /* Reset error state */ andre@1227: handleLTE(lteInvalidCertificate, true); andre@1339: mFailedConnections = 0; andre@1339: } else if (!isVisible()) { andre@1339: int waitInterval = getNextUpdateInterval(mFailedConnections++); andre@1339: if (waitInterval < 0) { andre@1339: qDebug() << "Shutting down after " << mFailedConnections << andre@1339: " tries to get an update connection."; andre@1339: closeApp(); andre@1339: return; andre@1339: } andre@1339: qDebug() << "Waiting for " << waitInterval / 1000 << " seconds until the next try."; andre@1339: QTimer::singleShot(waitInterval, this, SLOT(checkUpdates())); andre@1339: return; andre@708: } andre@1042: if ((getState() != NewSoftwareAvailable && getState() != NewListAvailable && mTrayMode) andre@1042: && !isVisible()) { andre@962: qDebug() << "Shutting down as no list or Software is available."; andre@962: closeApp(); andre@962: } else { andre@962: mTrayIcon->show(); andre@962: } andre@708: } andre@716: andre@716: int MainWindow::changeCount() andre@716: { andre@716: return mChangeCount; andre@716: } andre@722: andre@722: void MainWindow::setChangeCount(int cnt) andre@722: { andre@722: if (mChangeCount != cnt) { andre@722: mChangeCount = cnt; andre@722: emit changesChanged(QString("%1").arg(cnt)); andre@722: } andre@722: } andre@940: andre@956: void MainWindow::showProxySettings() andre@956: { andre@956: ProxySettingsDlg *dlg = new ProxySettingsDlg(this); andre@956: dlg->exec(); andre@956: } andre@956: andre@940: void MainWindow::showHelp() andre@940: { andre@940: char *inst_dir = get_install_dir(); andre@940: if (!inst_dir) { andre@940: qDebug() << "Failed to find install dir"; andre@940: return; andre@940: } andre@940: QString helpPath = QString::fromUtf8(inst_dir); andre@940: helpPath += HELP_PATH; andre@940: QFileInfo fiHelp(helpPath); andre@940: qDebug() << "Opening help: " << fiHelp.absoluteFilePath(); andre@940: if (!fiHelp.exists()) { andre@940: QMessageBox::warning(this, tr("Error!"), tr ("Failed to find the manual")); andre@940: return; andre@940: } andre@966: #ifdef Q_OS_WIN andre@966: QDesktopServices::openUrl(QUrl("file:///" + fiHelp.absoluteFilePath())); andre@966: #else andre@940: QDesktopServices::openUrl(QUrl(fiHelp.absoluteFilePath())); andre@966: #endif andre@940: free (inst_dir); andre@940: return; andre@940: } andre@1061: andre@1061: void MainWindow::showErrorMessage(const QString &msg) andre@1061: { andre@1061: QMessageBox::warning(this, tr("TrustBridge error"), msg); andre@1061: } andre@1227: andre@1227: void MainWindow::handleLTE(LongTimeErrors lte, bool reset) andre@1227: { andre@1227: QString settingPrefix; andre@1230: // qDebug() << "Handle LTE for " << lte << " Reset? : " << reset; andre@1227: switch (lte) { andre@1227: case lteInvalidSoftware: andre@1227: settingPrefix = "LTE/invalidSW"; andre@1227: break; andre@1227: case lteInvalidList: andre@1227: settingPrefix = "LTE/invalidList"; andre@1227: break; andre@1227: case lteInvalidCertificate: andre@1227: settingPrefix = "LTE/invalidCertificate"; andre@1227: break; andre@1227: case lteNoConnection: andre@1227: settingPrefix = "LTE/noConnection"; andre@1227: break; andre@1227: default: andre@1227: qDebug() << "Unhandled error. " << lte; andre@1227: } andre@1227: andre@1227: if (reset) { andre@1227: /* delete all values and be done */ andre@1227: mSettings.remove(settingPrefix + "_lastSaved"); andre@1227: mSettings.remove(settingPrefix + "_count"); andre@1230: mSettings.remove(settingPrefix + "_lastShown"); andre@1227: return; andre@1227: } andre@1227: andre@1227: QDateTime lastSaved = mSettings.value(settingPrefix + "_lastSaved").toDateTime(); andre@1227: bool cnt_valid; andre@1227: int cnt = mSettings.value(settingPrefix + "_count").toInt(&cnt_valid); andre@1227: if (!cnt_valid) { andre@1227: cnt = 0; andre@1227: } andre@1227: andre@1227: if (!lastSaved.isValid() || lastSaved.daysTo(QDateTime::currentDateTime()) >= 1) { andre@1227: /* The error count is increased at most once a day */ andre@1227: mSettings.setValue(settingPrefix + "_lastSaved", QDateTime::currentDateTime()); andre@1227: mSettings.setValue(settingPrefix + "_count", ++cnt); andre@1227: } andre@1227: andre@1227: andre@1227: if (cnt < 7) { andre@1227: /* We are done */ andre@1227: return; andre@1227: } andre@1227: /* A week has passed. Start showing the error. */ andre@1227: QDateTime lastShown = mSettings.value(settingPrefix + "_lastShown").toDateTime(); andre@1227: if (lastShown.isValid() && lastShown.daysTo(QDateTime::currentDateTime()) < 1) { andre@1227: /* Only show the error message once a day */ andre@1227: return; andre@1227: } andre@1227: andre@1227: mSettings.setValue(settingPrefix + "_lastShown", QDateTime::currentDateTime()); andre@1227: andre@1227: switch (lte) { andre@1227: case lteInvalidSoftware: andre@1227: showErrorMessage(tr("The integrity check for the available software update has " andre@1227: "failed repeatedly.") + "\n" + andre@1227: tr("Please contact your Support or the publisher of the Software.")); andre@1227: break; andre@1227: case lteInvalidList: andre@1227: showErrorMessage(tr("The integrity check of the available certificates has " andre@1227: "failed repeatedly.") + "\n" + andre@1227: tr("Please contact your Support or the publisher of the Software.")); andre@1227: break; andre@1227: case lteInvalidCertificate: emanuel@1275: showErrorMessage(tr("The authentication of the update server has " andre@1227: "failed repeatedly.") + "\n" + andre@1227: tr("Please contact your Support or the publisher of the Software.")); andre@1227: break; andre@1227: case lteNoConnection: andre@1227: bool useProxy = mSettings.value("UseProxy", false).toBool(); andre@1227: if (useProxy) { emanuel@1275: showErrorMessage(tr("The connection to the update server has " andre@1227: "failed repeatedly.") + "\n" + andre@1227: tr("Please check that the Proxy Server \"%1\" is available.").arg( andre@1227: mSettings.value("ProxyURL").toString())); andre@1227: } else { emanuel@1275: showErrorMessage(tr("The connection to the update server has " andre@1227: "failed repeatedly.") + "\n" + andre@1227: tr("Please check your internet connection.")); andre@1227: } andre@1227: break; andre@1227: } andre@1227: }