Mercurial > trustbridge
view ui/mainwindow.cpp @ 667:fb69aef056ea
Be more verbose in error logging
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Thu, 26 Jun 2014 17:41:53 +0200 |
parents | 911b94a475e0 |
children | 7147550ee15d |
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is Free Software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "mainwindow.h" #include <QDebug> #include <QProcess> #include <QProgressDialog> #include <QMessageBox> #include <QSystemTrayIcon> #include <QAction> #include <QDialog> #include <QDir> #include <QMenu> #include <QApplication> #include <QFile> #include <QTimer> #include <QHBoxLayout> #include <QVBoxLayout> #include <QGroupBox> #include <QSplitter> #include <QLabel> #include <QImage> #include <QCheckBox> #include <QButtonGroup> #include <QToolButton> #include "certificatelist.h" #include "downloader.h" #include "helpdialog.h" #include "aboutdialog.h" #include "certificateitemdelegate.h" #include "separatoritemdelegate.h" #include "installwrapper.h" #include "util.h" #include "logging.h" #include "binverify.h" #include "processhelp.h" #include "processwaitdialog.h" // The amount of time in minutes stay silent if we have // something to say #define NAG_INTERVAL_MINUTES 70 #ifndef APPNAME #define APPNAME "TrustBridge" #endif #define SERVER_URL "https://files.intevation.de:443" #ifdef RELEASE_BUILD # define LIST_RESOURCE "/users/aheinecke/zertifikatsliste.txt" # ifdef Q_OS_WIN # define SW_RESOURCE_VERSION "/users/aheinecke/TrustBridge-%1.exe" # define SW_RESOURCE "/users/aheinecke/TrustBridge.exe" # else # define SW_RESOURCE_VERSION "/users/aheinecke/TrustBridge-%1.sh" # define SW_RESOURCE "/users/aheinecke/TrustBridge.sh" # endif #else // RELEASE_BUILD # define LIST_RESOURCE "/users/aheinecke/development/zertifikatsliste.txt" # ifdef Q_OS_WIN # define SW_RESOURCE_VERSION "/users/aheinecke/development/TrustBridge-development.exe" # define SW_RESOURCE "/users/aheinecke/development/TrustBridge.exe" # else # define SW_RESOURCE_VERSION "/users/aheinecke/development/TrustBridge-development.sh" # define SW_RESOURCE "/users/aheinecke/development/TrustBridge.sh" # endif #endif MainWindow::MainWindow(bool trayMode): mTrayMode(trayMode) { createActions(); createTrayIcon(); createContent(); resize(950, 540); qRegisterMetaType<SSLConnection::ErrorCode>("SSLConnection::ErrorCode"); qRegisterMetaType<Certificate::Status>("Certificate::Status"); connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); mMessageTimer = new QTimer(this); connect(mMessageTimer, SIGNAL(timeout()), this, SLOT(showMessage())); mMessageTimer->setInterval(NAG_INTERVAL_MINUTES * 60 * 1000); mMessageTimer->start(); checkUpdates(); loadUnselectedCertificates(); loadCertificateList(); if (!trayMode) { show(); } } void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: case QSystemTrayIcon::MiddleClick: showMessage(); break; case QSystemTrayIcon::DoubleClick: show(); break; default: ; } } void MainWindow::messageClicked() { if (mCurState == NewListAvailable) { show(); } if (mCurState == NewSoftwareAvailable) { verifySWData(); QString swFileName = mSettings.value("Software/available").toString(); if (swFileName.isEmpty()) { checkUpdates(true); mCurState = DownloadingSW; return; } installNewSW(swFileName, mSettings.value("Software/availableDate").toDateTime()); } } void MainWindow::showMessage() { if (mCurMessage.isEmpty()) { return; } if (mCurState == NewSoftwareAvailable || !isVisible()) { mTrayIcon->showMessage(QApplication::applicationName(), mCurMessage, QSystemTrayIcon::Information, 10000); mMessageTimer->start(); // Restart the timer so that we don't spam } } void MainWindow::verifyListData() { QString availableFileName = mSettings.value("List/available").toString(); QString installedFileName = mSettings.value("List/installed").toString(); if (!availableFileName.isEmpty()) { mListToInstall.readList(availableFileName.toLocal8Bit().constData()); if (!mListToInstall.isValid()) { mCurState = TransferError; // Probably a bug when Qt fileName is encoded and cFileName // fails because of this. This needs a unit test! // Maybe check that the file is in our data directory QFile::remove(availableFileName); mSettings.remove("List/available"); mSettings.remove("List/availableDate"); } } else { // Make sure the available notation is also removed mSettings.remove("List/available"); mSettings.remove("List/availableDate"); } if (!installedFileName.isEmpty()) { mInstalledList.readList(installedFileName.toLocal8Bit().constData()); if (!mInstalledList.isValid()) { // Probably a bug when Qt fileName is encoded and cFileName // fails because of this. This needs a unit test! // Maybe check that the file is in our data directory QFile::remove(installedFileName); mSettings.remove("List/installed"); mSettings.remove("List/installedDate"); } } else { mSettings.remove("List/installed"); mSettings.remove("List/installedDate"); } } void MainWindow::verifySWData() { QString swFileName = mSettings.value("Software/available").toString(); if (swFileName.isEmpty()) { mSettings.remove("Software/availableDate"); return; } QFileInfo fi(swFileName); if (!fi.exists()) { mSettings.remove("Software/available"); mSettings.remove("Software/availableDate"); qDebug() << "Software does not yet exist."; return; } if (!fi.isExecutable()) { qWarning() << "Downloaded file: " << swFileName << " is not executable."; QFile::remove(swFileName); mSettings.remove("Software/available"); mSettings.remove("Software/availableDate"); return; } bin_verify_result verifyResult = verify_binary(swFileName.toUtf8().constData(), swFileName.toUtf8().size()); qDebug() << "Binary verify result: " << verifyResult; if (verifyResult != VerifyValid) { qDebug() << "Failed to verify downloaded data."; QFile::remove(swFileName); mSettings.remove("Software/available"); mSettings.remove("Software/availableDate"); return; } } void MainWindow::handleNewList(const QString& fileName, const QDateTime& modDate) { qDebug() << "new list available"; mSettings.setValue("List/available", fileName); mSettings.setValue("List/availableDate", modDate); verifyListData(); if (!mListToInstall.isValid()) { /* Downloader provided invalid files */ /* TODO: Error count. Error handling. Otherwise * we can go into an endless loop here */ /* Retry the download again in 10 - 20 minutes */ QTimer::singleShot(600000 + (qrand() % 60000), this, SLOT(checkUpdates())); } else { mCurMessage = tr("An updated certificate list is available. Click here to install."); setState(NewListAvailable); showMessage(); loadCertificateList(); } } void MainWindow::handleNewSW(const QString& fileName, const QDateTime& modDate) { 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); mSettings.setValue("Software/availableDate", modDate); mSettings.sync(); showMessage(); } void MainWindow::installNewSW(const QString& fileName, const QDateTime& modDate) { QFileInfo instProcInfo = QFileInfo(fileName); QString filePath = QDir::toNativeSeparators(instProcInfo.absoluteFilePath()); if (verify_binary(filePath.toUtf8().constData(), filePath.toUtf8().size()) != VerifyValid) { qDebug() << "Invalid software. Not installing"; return; } QFileInfo fi(QCoreApplication::applicationFilePath()); QDir installDir = fi.absoluteDir(); #ifdef WIN32 QString parameters = QString::fromLatin1("/S /UPDATE=1 /D=") + installDir.path().replace("/", "\\") + ""; SHELLEXECUTEINFOW shExecInfo; memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOW)); shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shExecInfo.lpFile = reinterpret_cast<LPCWSTR> (filePath.utf16()); shExecInfo.lpParameters = reinterpret_cast<LPCWSTR> (parameters.utf16()); // shExecInfo.fMask = SEE_MASK_NOASYNC; shExecInfo.nShow = SW_SHOWDEFAULT; if (!is_admin()) { shExecInfo.lpVerb = L"open"; } else { shExecInfo.lpVerb = L"runas"; } qDebug() << "Starting process: " << filePath << " with arguments: " << parameters; 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 */ QStringList parameters; parameters << "/S" << "/UPDATE=1" << QString::fromLatin1("/D=%1").arg(installDir.path()); qDebug() << "Starting process " << filePath << " args: " << parameters; if (!QProcess::startDetached(filePath, parameters)) { qDebug() << "Failed to start process."; return; } #endif /* Installer process should now be running. We exit */ closeApp(); } void MainWindow::checkUpdates(bool downloadSW) { verifyListData(); 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(); QDateTime swInstalledLastMod = mSettings.value("Software/installedDate").toDateTime(); QString listResource = QString::fromLatin1(LIST_RESOURCE); QString swResource = QString::fromLatin1(SW_RESOURCE); #ifndef RELEASE_BUILD /* Use this for testing to specify another file name for updates */ listResource = mSettings.value("List/resource", listResource).toString(); swResource = mSettings.value("Software/resource", swResource).toString(); #endif Downloader* downloader = new Downloader(this, QString::fromLatin1(SERVER_URL), QByteArray(), swInstalledLastMod, listInstalledLastMod, swResource, listResource, downloadSW); connect(downloader, SIGNAL(newListAvailable(const QString&, const QDateTime&)), this, SLOT(handleNewList(const QString&, const QDateTime&))); if (!downloadSW) { connect(downloader, SIGNAL(newSoftwareAvailable(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&))); } 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::getLastModForCurrentVersion() { QString softwareVersion = QString::fromLatin1(SW_RESOURCE_VERSION).arg( QApplication::applicationVersion()); qDebug() << softwareVersion; QString listResource = QString::fromLatin1(LIST_RESOURCE); Downloader* downloader = new Downloader(this, QString::fromLatin1(SERVER_URL), QByteArray(), QDateTime::currentDateTime(), QDateTime::currentDateTime(), softwareVersion, listResource, false); connect(downloader, SIGNAL(finished()), downloader, SLOT(deleteLater())); connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)), this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode))); connect(downloader, SIGNAL(lastModifiedDate(const QDateTime&)), this, SLOT(setLastModifiedSWDate(const QDateTime&))); downloader->start(); } void MainWindow::setLastModifiedSWDate(const QDateTime &date) { mSettings.beginGroup("Software"); mSettings.setValue("installedDate", date); mSettings.setValue("installedVersion", QApplication::applicationVersion()); mSettings.endGroup(); checkUpdates(); } 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); connect(mCheckUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates())); mQuitAction = new QAction(tr("Quit"), this); connect(mQuitAction, SIGNAL(triggered()), qApp, SLOT(quit())); } void MainWindow::createTrayIcon() { QIcon trayImg(":/img/tray_22.png"); mTrayMenu = new QMenu(this); mTrayMenu->addAction(mCheckUpdates); mTrayMenu->addAction(mQuitAction); mTrayIcon = new QSystemTrayIcon(this); mTrayIcon->setContextMenu(mTrayMenu); mTrayIcon->setIcon(trayImg); setWindowIcon(trayImg); mTrayIcon->show(); mTrayIcon->setToolTip(tr("TrustBridge")); connect(mTrayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked())); } void MainWindow::createContent() { // Create a central widget containing the main layout. QWidget *base = new QWidget; // Layouts and Container QVBoxLayout *mainLayout = new QVBoxLayout; QHBoxLayout *headerLayout = new QHBoxLayout; QVBoxLayout *headerTextLayout = new QVBoxLayout; QHBoxLayout *centerLayout = new QHBoxLayout; QVBoxLayout *buttonBarLayout = new QVBoxLayout; QHBoxLayout *bottomLayout = new QHBoxLayout; QHBoxLayout *containerLayout = new QHBoxLayout; // The header (icon, about text) QImage *logoImage = new QImage(":/img/logo.png"); QLabel *logo = new QLabel; logo->setBackgroundRole(QPalette::Base); logo->setPixmap(QPixmap::fromImage(*logoImage)); QLabel *title = new QLabel("<h1>" + QString::fromLatin1(APPNAME) + "</h1>"); QLabel *subTitle = new QLabel("Trust in your digital communication"); headerTextLayout->addWidget(title); headerTextLayout->addWidget(subTitle); headerLayout->addWidget(logo); headerLayout->addLayout(headerTextLayout); headerLayout->setStretch(0, 0); headerLayout->setStretch(1, 10); /*********************************** * The Buttonbar on the left side. ***********************************/ mButtonGroup = new QButtonGroup; QToolButton *updatesButton = new QToolButton; updatesButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); updatesButton->setIcon(QIcon(":/img/view-refresh.png")); updatesButton->setIconSize(QSize(32, 32)); updatesButton->setText(tr("Updates")); updatesButton->setFixedWidth(120); updatesButton->setFixedHeight(75); updatesButton->setCheckable(true); updatesButton->setChecked(true); QToolButton *allInstallButton = new QToolButton; allInstallButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); allInstallButton->setIcon(QIcon(":/img/document-encrypt.png")); allInstallButton->setIconSize(QSize(32, 32)); allInstallButton->setText(tr("Trusted\ncertificates")); allInstallButton->setFixedWidth(120); allInstallButton->setFixedHeight(75); allInstallButton->setCheckable(true); QToolButton *allRemoveButton = new QToolButton; allRemoveButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); allRemoveButton->setIcon(QIcon(":/img/task-attempt.png")); allRemoveButton->setIconSize(QSize(32, 32)); allRemoveButton->setText(tr("Revoked\ncertificates")); allRemoveButton->setFixedWidth(120); allRemoveButton->setFixedHeight(75); allRemoveButton->setCheckable(true); QToolButton *infoButton = new QToolButton; infoButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); infoButton->setIcon(QIcon(":/img/dialog-information.png")); infoButton->setIconSize(QSize(32, 32)); infoButton->setText(tr("Information\nand help")); infoButton->setFixedWidth(120); infoButton->setFixedHeight(75); infoButton->setCheckable(true); mButtonGroup->addButton(updatesButton); mButtonGroup->addButton(allInstallButton); mButtonGroup->addButton(allRemoveButton); mButtonGroup->addButton(infoButton); mButtonGroup->setId(updatesButton, 0); mButtonGroup->setId(allInstallButton, 1); mButtonGroup->setId(allRemoveButton, 2); mButtonGroup->setId(infoButton, 3); connect(mButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(togglePages(int))); buttonBarLayout->addWidget(updatesButton); buttonBarLayout->addWidget(allInstallButton); buttonBarLayout->addWidget(allRemoveButton); buttonBarLayout->insertStretch(3, 10); buttonBarLayout->addWidget(infoButton); /******************************** * The main pages. ********************************/ /******************************** * The updates page. ********************************/ QVBoxLayout *updatesMainLayout = new QVBoxLayout; mUpdatesPanel = new QScrollArea; QScrollArea *updatesContent = new QScrollArea; // updatesContent->setFrameShape(QFrame::NoFrame); mUpdatesWidget = new QWidget; /* The updates header */ QHBoxLayout *updatesHeaderLayout = new QHBoxLayout; QVBoxLayout *updatesHeaderTextLayout = new QVBoxLayout; QVBoxLayout *updatesHeaderButtonLayout = new QVBoxLayout; QHBoxLayout *updatesHeaderActionLayout = new QHBoxLayout; QHBoxLayout *updatesHeaderSearchLayout = new QHBoxLayout; mUpdatesHeader = new QLabel("<h2>" + tr("Updates (%1/%2)").arg(0).arg(0)+ "</h2>"); mLastCertUpdate = new QLabel(tr("Last update of certificates: %1").arg("")); mLastSWupdate = new QLabel(tr("Last update of TrustBridge: %1").arg("")); QLabel *updatesTip = new QLabel(tr("You should apply the following changes to your root certificates:")); updatesHeaderTextLayout->addWidget(mUpdatesHeader); updatesHeaderTextLayout->addWidget(mLastCertUpdate); updatesHeaderTextLayout->addWidget(mLastSWupdate); updatesHeaderTextLayout->addSpacing(10); updatesHeaderTextLayout->addWidget(updatesTip); QPushButton *searchUpdates = new QPushButton(tr("Check for updates")); searchUpdates->setIcon(QIcon(":/img/system-search.png")); QPushButton *quitButton = new QPushButton(tr("Quit without saving")); quitButton->setIcon(QIcon(":/img/system-shutdown.png")); QPushButton *installButton = new QPushButton(tr("Install updates")); #ifdef Q_OS_WIN if (is_admin()) { QIcon uacShield = QApplication::style()->standardIcon(QStyle::SP_VistaShield); installButton->setIcon(uacShield); } #else installButton->setIcon(QIcon(":/img/edit-redo.png")); #endif connect(quitButton, SIGNAL(clicked()), this, SLOT(closeApp())); connect(installButton, SIGNAL(clicked()), this, SLOT(checkAndInstallCerts())); connect(searchUpdates, SIGNAL(clicked()), this, SLOT(checkUpdates())); updatesHeaderActionLayout->addWidget(installButton); updatesHeaderActionLayout->addWidget(quitButton); updatesHeaderSearchLayout->insertStretch(0, 1); updatesHeaderSearchLayout->addWidget(searchUpdates); updatesHeaderButtonLayout->addLayout(updatesHeaderSearchLayout); updatesHeaderButtonLayout->addLayout(updatesHeaderActionLayout); updatesHeaderLayout->addLayout(updatesHeaderTextLayout); updatesHeaderLayout->insertStretch(1, 10); updatesHeaderLayout->addLayout(updatesHeaderButtonLayout); /* The central panels. */ QVBoxLayout *updatesCenterLayout = new QVBoxLayout; QHBoxLayout *updatesNewLayout = new QHBoxLayout; QHBoxLayout *updatesRemoveLayout = new QHBoxLayout; QHBoxLayout *updatesManualLayout = new QHBoxLayout; mUpdatesNewCertificates = new QLabel("<h3>" + tr("Install new trusted certificates (%1/%2)").arg(0).arg(0) + "</h3>"); mUpdatesDetailsNew = new QPushButton(tr("Show details")); connect(mUpdatesDetailsNew, SIGNAL(clicked()), this, SLOT(toggleUpdatesNew())); updatesNewLayout->addWidget(mUpdatesNewCertificates); updatesNewLayout->addWidget(mUpdatesDetailsNew); updatesNewLayout->insertStretch(2, 10); mUpdatesNew = new CertificateListWidget(this); connect(mUpdatesNew, SIGNAL(certListChanged(int)), this, SLOT(listChanged(int))); mUpdatesNew->hide(); mUpdatesRemoveCertificates = new QLabel("<h3>" + tr("Remove revoked certificates (%1/%2)").arg(0).arg(0) + "</h3>"); mUpdatesDetailsRemove = new QPushButton(tr("Show details")); connect(mUpdatesDetailsRemove, SIGNAL(clicked()), this, SLOT(toggleUpdatesRemove())); updatesRemoveLayout->addWidget(mUpdatesRemoveCertificates); updatesRemoveLayout->addWidget(mUpdatesDetailsRemove); updatesRemoveLayout->insertStretch(2, 10); mUpdatesRemove = new CertificateListWidget(this); connect(mUpdatesRemove, SIGNAL(certListChanged(int)), this, SLOT(listChanged(int))); mUpdatesRemove->hide(); mUpdatesManualCertificates = new QLabel("<h3>" + tr("Manually changed certificates (%1)").arg(0) + "</h3>"); mUpdatesDetailsManual = new QPushButton(tr("Show details")); connect(mUpdatesDetailsManual, SIGNAL(clicked()), this, SLOT(toggleUpdatesManual())); updatesManualLayout->addWidget(mUpdatesManualCertificates); updatesManualLayout->addWidget(mUpdatesDetailsManual); updatesManualLayout->insertStretch(2, 10); mUpdatesManual = new CertificateListWidget(this); mUpdatesManual->hide(); connect(mUpdatesManual, SIGNAL(certChanged(bool, const Certificate&)), this, SLOT(removeFromManual(bool, const Certificate&))); updatesNewLayout->setAlignment(Qt::AlignTop); updatesRemoveLayout->setAlignment(Qt::AlignTop); updatesManualLayout->setAlignment(Qt::AlignTop); updatesCenterLayout->addLayout(updatesNewLayout); updatesCenterLayout->addWidget(mUpdatesNew); updatesCenterLayout->addLayout(updatesRemoveLayout); updatesCenterLayout->addWidget(mUpdatesRemove); updatesCenterLayout->addLayout(updatesManualLayout); updatesCenterLayout->addWidget(mUpdatesManual); updatesCenterLayout->addStretch(1); updatesCenterLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); mUpdatesWidget->setLayout(updatesCenterLayout); updatesContent->setWidget(mUpdatesWidget); updatesMainLayout->addLayout(updatesHeaderLayout); updatesMainLayout->addWidget(updatesContent); mUpdatesPanel->setLayout(updatesMainLayout); /********************************* * Panel for trusted certificates. *********************************/ mInstallPanel = new QScrollArea; QVBoxLayout *installPanelLayout = new QVBoxLayout; QVBoxLayout *installHeaderLayout = new QVBoxLayout; QVBoxLayout *installCenterLayout = new QVBoxLayout; QLabel *installHeaderLabel = new QLabel("<h2>" + tr("Trusted certificates") + "</h2>"); QLabel *installHeaderText = new QLabel(tr("The following list of trusted root" " certificates is managed by the BSI. The BSI validates independently the" " authenticity, security and actuality of these certificates.")); installHeaderLayout->addWidget(installHeaderLabel); installHeaderLayout->addWidget(installHeaderText); QLabel *installCenterText = new QLabel(tr("Please choose the certificates" " you want to trust or untrust. TrustBridge will install these certificates for your" " secure communication for email and internet.")); installCenterLayout->addWidget(installCenterText); mInstallList = new CertificateListWidget(this); connect(mInstallList, SIGNAL(certChanged(bool, const Certificate&)), this, SLOT(toggleInManual(bool, const Certificate&))); installPanelLayout->addLayout(installHeaderLayout); installPanelLayout->addLayout(installCenterLayout); installPanelLayout->addWidget(mInstallList); mInstallPanel->setLayout(installPanelLayout); /********************************** * Panel for certificates to be removed. **********************************/ mRemovePanel = new QScrollArea; QVBoxLayout *removePanelLayout = new QVBoxLayout; QVBoxLayout *removeHeaderLayout = new QVBoxLayout; QVBoxLayout *removeCenterLayout = new QVBoxLayout; QLabel *removeHeaderLabel = new QLabel("<h2>" + tr("Revoked certificates") + "</h2>"); QLabel *removeHeaderText = new QLabel(tr("Certificates can be corrupted" " or stolen and misused in many ways. Therefore the BSI recommends" " to remove all revoked certificates from your system.")); removeHeaderLayout->addWidget(removeHeaderLabel); removeHeaderLayout->addWidget(removeHeaderText); QLabel *removeCenterText = new QLabel(tr("The follwing unsecure certificates were" " revoked by the BSI. Already uninstalled certificates cannot be reinstalled." " It is recommended that you select all certificates to uninstall if you still" " have revoked certificates installed.")); removeCenterLayout->addWidget(removeCenterText); mRemoveList = new CertificateListWidget(this); connect(mRemoveList, SIGNAL(certChanged(bool, const Certificate&)), this, SLOT(toggleInManual(bool, const Certificate&))); removePanelLayout->addLayout(removeHeaderLayout); removePanelLayout->addLayout(removeCenterLayout); removePanelLayout->addWidget(mRemoveList); mRemovePanel->setLayout(removePanelLayout); /********************************** * The info panel. **********************************/ mInfoPanel = new QScrollArea; QVBoxLayout *infoPanelLayout = new QVBoxLayout; QHBoxLayout *infoHeaderLayout = new QHBoxLayout; QVBoxLayout *infoHeaderTextLayout = new QVBoxLayout; QVBoxLayout *infoCenterLayout = new QVBoxLayout; QImage *infoLogoImage = new QImage(":/img/logo.png"); QLabel *infoLogo = new QLabel; infoLogo->setBackgroundRole(QPalette::Base); infoLogo->setPixmap(QPixmap::fromImage(*infoLogoImage)); QLabel *infoTitle = new QLabel("<h1>" + tr("TrustBridge") + "</h1>"); QString infoVersion = tr("Version: "); infoVersion.append(QApplication::applicationVersion()); QLabel *appVersion = new QLabel(infoVersion); appVersion->setTextInteractionFlags( Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); QFrame *infoHeaderSeparator = new QFrame(); infoHeaderSeparator->setFrameShape(QFrame::HLine); infoHeaderSeparator->setFrameShadow(QFrame::Sunken); infoHeaderTextLayout->addWidget(infoTitle); infoHeaderTextLayout->addWidget(appVersion); infoHeaderLayout->addWidget(infoLogo); infoHeaderLayout->addLayout(infoHeaderTextLayout); infoHeaderLayout->insertStretch(2, 10); QLabel *textDesc = new QLabel(tr("TrustBridge is a root certificate" " installer for Windows and Linux.")); textDesc->setTextInteractionFlags( Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); QLabel *textManage = new QLabel(tr("The root certificate lists are managed" " by the German <a href=\"https://www.bsi.bund.de\">" "Federal Office for Information Security (BSI)</a>.\n\n")); textManage->setTextInteractionFlags( Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); QLabel *textDevel = new QLabel(tr("The software was developed by the companies" " <a href=\"http://www.intevation.de\">Intevation GmbH</a> and " " <a href=\"http://www.dn-systems.de\">DN-Systems GmbH</a>, <br>" " contracted by the German Federal Office for Information Security (BSI).\n\n")); textDevel->setTextInteractionFlags( Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); QLabel *textLicense = new QLabel(tr("TrustBridge is Free Software licensed" " under GNU GPL v2+.\n\nCopyright (C) 2014 by Bundesamt für Sicherheit" " in der Informationstechnik")); textLicense->setTextInteractionFlags( Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); infoCenterLayout->addWidget(infoHeaderSeparator); infoCenterLayout->addWidget(textDesc); infoCenterLayout->addWidget(textManage); infoCenterLayout->addWidget(textDevel); infoCenterLayout->addWidget(textLicense); infoCenterLayout->insertSpacing(2, 10); infoCenterLayout->insertSpacing(4, 10); infoCenterLayout->insertSpacing(6, 10); infoCenterLayout->insertStretch(8, 10); infoPanelLayout->addLayout(infoHeaderLayout); infoPanelLayout->addLayout(infoCenterLayout); mInfoPanel->setLayout(infoPanelLayout); /******************************** * The main layout for pages. ********************************/ mInstallPanel->hide(); mRemovePanel->hide(); mInfoPanel->hide(); containerLayout->addWidget(mUpdatesPanel); containerLayout->addWidget(mInstallPanel); containerLayout->addWidget(mRemovePanel); containerLayout->addWidget(mInfoPanel); centerLayout->addLayout(buttonBarLayout); centerLayout->addLayout(containerLayout); mainLayout->addLayout(headerLayout); mainLayout->addLayout(centerLayout); mainLayout->addLayout(bottomLayout); base->setLayout(mainLayout); setCentralWidget(base); } void MainWindow::listChanged(int selected) { mUpdatesHeader->setText("<h2>" + tr("Updates (%1/%2)") .arg(mUpdatesRemove->selectedCertCount() + mUpdatesNew->selectedCertCount()) .arg(mUpdatesRemove->certificates().size() + mUpdatesNew->certificates().size()) + "</h2>"); mUpdatesNewCertificates->setText("<h3>" + tr("Install new trusted certificates (%1/%2)") .arg(mUpdatesNew->selectedCertCount()) .arg(mUpdatesNew->certificates().size()) + "</h3>"); mUpdatesRemoveCertificates->setText("<h3>" + tr("Remove revoked certificates (%1/%2)") .arg(mUpdatesRemove->selectedCertCount()) .arg(mUpdatesRemove->certificates().size()) + "</h3>"); } void MainWindow::loadCertificateList() { /* TODO: if nothing is available (neither old nor new) add some progress * indication */ mInstallList->clear(); mUpdatesNew->clear(); mRemoveList->clear(); mUpdatesRemove->clear(); QList<Certificate> newInstallCerts; QList<Certificate> newRemoveCerts; QList<Certificate> oldInstallCerts; QList<Certificate> oldRemoveCerts; if (mListToInstall.getCertificates().isEmpty()) { // No new list available, add old certificates. foreach (const Certificate &cert, mInstalledList.getCertificates()) { bool state = !mPreviouslyUnselected.contains(cert.base64Line()); if (cert.isInstallCert()) { oldInstallCerts.append(cert); mInstallList->addCertificate(cert, state); } else { oldRemoveCerts.append(cert); mRemoveList->addCertificate(cert, state, !state); } } } else { // Sort and filter both lists. foreach (const Certificate &cert, mListToInstall.getCertificates()) { bool state = !mPreviouslyUnselected.contains(cert.base64Line()); if (cert.isInstallCert()) { // Certificate with status "install". if (mInstalledList.getCertificates().contains(cert)) { // Was in the old list. oldInstallCerts.append(cert); mInstallList->addCertificate(cert, state); } else { // Is a brand new certificate newInstallCerts.append(cert); mUpdatesNew->addCertificate(cert, state); } } else { // Certificate with status "remove". if (mInstalledList.getCertificates().contains(cert)) { // Was in the old list. oldRemoveCerts.append(cert); // Is removed, so set editable to false. mRemoveList->addCertificate(cert, state, !state); } else { // Was in the old list with status "install" and now has the // status "remove". newRemoveCerts.append(cert); mUpdatesRemove->addCertificate(cert, state); } } } mUpdatesHeader->setText("<h2>" + tr("Updates (%1/%2)") .arg(mUpdatesNew->selectedCertCount() + mUpdatesRemove->selectedCertCount()) .arg(newRemoveCerts.size() + newInstallCerts.size()) + "</h2>"); mLastCertUpdate->setText(tr("Last update of certificates: %1") .arg(mInstalledList.date().toString())); } mUpdatesNewCertificates->setText("<h3>" + tr("Install new trusted certificates (%1/%2)") .arg(mUpdatesNew->selectedCertCount()) .arg(mUpdatesNew->certificates().size()) + "</h3>"); mUpdatesRemoveCertificates->setText("<h3>" + tr("Remove revoked certificates (%1/%2)") .arg(mUpdatesRemove->selectedCertCount()) .arg(mUpdatesRemove->certificates().size()) + "</h3>"); mUpdatesManualCertificates->setText("<h3>" + tr("Manually changed certificates (%1)").arg(0) + "</h3>"); } void MainWindow::installerError(const QString& errMsg) { QMessageBox::warning(this, tr("Error executing update"), errMsg); } void MainWindow::installerSuccess() { if (mCurState == NewListAvailable) { mCurState = NothingChanged; mCurMessage = QString(); QString listFileName = mSettings.value("List/available").toString(); QDateTime listFileDate = mSettings.value("List/availableDate").toDateTime(); mSettings.remove("List/available"); mSettings.remove("List/availableDate"); if (listFileName.isEmpty() || !listFileDate.isValid()) { qWarning() << "Error accessing settings"; return; /* Try again with next check */ } mSettings.setValue("List/installed", listFileName); mSettings.setValue("List/installedDate", listFileDate); mInstalledList = mListToInstall; mListToInstall = CertificateList(); } loadCertificateList(); } void MainWindow::installCerts() { QStringList choices; QStringList unselected; choices << mUpdatesNew->selectedCertificates(); choices << mUpdatesRemove->selectedCertificates(); choices << mUpdatesManual->selectedCertificates(); QStringList unselectedManuals = mUpdatesManual->unselectedCertificates(); for(int i = 0; i < unselectedManuals.size(); i++) { if (unselectedManuals.at(i).startsWith("I:")) { QString certLine = unselectedManuals.at(i); certLine[0] = 'R'; choices << certLine; } } unselected << mUpdatesNew->unselectedCertificates(); unselected << mUpdatesRemove->unselectedCertificates(); unselected << mInstallList->unselectedCertificates(); unselected << mRemoveList->unselectedCertificates(); QProgressDialog *progress = new QProgressDialog(this); progress->setWindowModality(Qt::WindowModal); progress->setLabelText(tr("Installing certificates...")); progress->setCancelButton(0); progress->setRange(0,0); progress->setMinimumDuration(0); progress->show(); InstallWrapper *instWrap = new InstallWrapper(this, mListToInstall.isValid() ? mListToInstall.fileName() : mInstalledList.fileName(), choices); /* Clean up object and progress dialog */ connect(instWrap, SIGNAL(finished()), instWrap, SLOT(deleteLater())); connect(instWrap, SIGNAL(finished()), progress, SLOT(deleteLater())); connect(instWrap, SIGNAL(finished()), progress, SLOT(cancel())); connect(instWrap, SIGNAL(installationSuccessful()), this, SLOT(installerSuccess())); connect(instWrap, SIGNAL(error(const QString &)), this, SLOT(installerError(const QString &))); instWrap->start(); if (!saveUnselectedCertificates(unselected)) { qWarning() << "Failed to save previosly unselected certificates."; } } void MainWindow::loadUnselectedCertificates() { mPreviouslyUnselected.clear(); mSettings.beginGroup("unselected"); QStringList keys = mSettings.allKeys(); foreach (const QString &key, keys) { mPreviouslyUnselected << mSettings.value(key, QString()).toString(); } mSettings.endGroup(); } bool MainWindow::saveUnselectedCertificates(QStringList unselected) { mPreviouslyUnselected.clear(); mSettings.beginGroup("unselected"); mSettings.remove(""); /* Clears old choices */ for (int i = 0; i < unselected.size(); i++) { QString key = QString::fromLatin1("cert%1").arg(i); QString value = unselected.at(i); mSettings.setValue(key, value); mPreviouslyUnselected << value; } mSettings.endGroup(); mSettings.sync(); return mSettings.status() == QSettings::NoError; } void MainWindow::toggleInManual(bool state, const Certificate &cert) { if (!mUpdatesManual->contains(cert)) { mUpdatesManual->addCertificate(cert, state); } else { mUpdatesManual->removeCertificate(cert); } mUpdatesManualCertificates->setText("<h3>" + tr("Manually changed certificates (%1)") .arg(mUpdatesManual->certificates().size()) + "</h3>"); } void MainWindow::removeFromManual(bool state, const Certificate &cert) { mUpdatesManual->removeCertificate(cert); mUpdatesManualCertificates->setText("<h3>" + tr("Manually changed certificates (%1)") .arg(mUpdatesManual->certificates().size()) + "</h3>"); if (cert.isInstallCert()) { mInstallList->setCertState(state, cert); } else { mRemoveList->setCertState(state, cert); } } void MainWindow::closeApp() { qApp->quit(); } void MainWindow::checkAndInstallCerts() { /* Checking before opening the dialog should be cheaper */ QList<int> pids = ProcessHelp::getProcessesIdForName("firefox"); pids.append(ProcessHelp::getProcessesIdForName("thunderbird")); if (pids.isEmpty()) { installCerts(); return; } QStringList pNames; pNames << "firefox" << "thunderbird"; ProcessWaitDialog *waitDialog = new ProcessWaitDialog(this, pNames); connect(waitDialog, SIGNAL(accepted()), this, SLOT(installCerts())); connect(waitDialog, SIGNAL(accepted()), waitDialog, SLOT(deleteLater())); waitDialog->exec(); return; } void MainWindow::togglePages(int button) { mUpdatesPanel->hide(); mInstallPanel->hide(); mRemovePanel->hide(); mInfoPanel->hide(); switch(button) { case 0: mUpdatesPanel->show(); break; case 1: mInstallPanel->show(); break; case 2: mRemovePanel->show(); break; case 3: mInfoPanel->show(); break; default: mUpdatesPanel->show(); break; } return; } void MainWindow::toggleUpdatesNew() { if (!mUpdatesNew->isVisible()) { mUpdatesDetailsNew->setText(tr("Hide details")); mUpdatesNew->show(); mUpdatesNew->setSelected(0); } else { mUpdatesNew->hide(); mUpdatesDetailsNew->setText(tr("Show details")); QSize old = mUpdatesWidget->size(); mUpdatesWidget->resize(old.width(), old.height() - mUpdatesNew->height()); } } void MainWindow::toggleUpdatesRemove() { if (!mUpdatesRemove->isVisible()) { mUpdatesDetailsRemove->setText(tr("Hide details")); mUpdatesRemove->show(); mUpdatesRemove->setSelected(0); } else { mUpdatesRemove->hide(); mUpdatesDetailsRemove->setText(tr("Show details")); QSize old = mUpdatesWidget->size(); mUpdatesWidget->resize(old.width(), old.height() - mUpdatesRemove->height()); } } void MainWindow::toggleUpdatesManual() { if (!mUpdatesManual->isVisible()) { mUpdatesDetailsManual->setText(tr("Hide details")); mUpdatesManual->show(); mUpdatesManual->setSelected(0); } else { mUpdatesDetailsManual->setText(tr("Show details")); mUpdatesManual->hide(); QSize old = mUpdatesWidget->size(); mUpdatesWidget->resize(old.width(), old.height() - mUpdatesManual->height()); } }