Mercurial > trustbridge
view ui/mainwindow.cpp @ 929:613a0e24f316
(issue72) Added png icon for red circle.
author | Emanuel Schuetze <emanuel@intevation.de> |
---|---|
date | Fri, 22 Aug 2014 14:32:33 +0200 |
parents | ca997bc0f790 |
children | 57371f2e8dae |
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 <QAction> #include <QDialog> #include <QDir> #include <QMenu> #include <QApplication> #include <QFile> #include <QTimer> #include <QHBoxLayout> #include <QVBoxLayout> #include <QGridLayout> #include <QGroupBox> #include <QSplitter> #include <QLabel> #include <QImage> #include <QCheckBox> #include <QButtonGroup> #include <QToolButton> #include <QStandardPaths> #include "certificatelist.h" #include "downloader.h" #include "helpdialog.h" #include "aboutdialog.h" #include "separatoritemdelegate.h" #include "installwrapper.h" #include "util.h" #include "logging.h" #include "binverify.h" #include "processhelp.h" #include "processwaitdialog.h" #include "trayicon.h" // The amount of time in minutes stay silent if we have // something to say #define NAG_INTERVAL_MINUTES 70 #define DATETIME_FORMAT "dddd, d.MMMM yyyy HH:mm:ss" #ifndef APPNAME #define APPNAME "TrustBridge" #endif #define SERVER_URL "https://files.intevation.de:443" #if defined(_X86_) || defined(__i386__) #define TB_ARCH_STRING "-i386" #else #define TB_ARCH_STRING "-amd64" #endif #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" TB_ARCH_STRING ".sh" # define SW_RESOURCE "/users/aheinecke/TrustBridge" TB_ARCH_STRING ".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" TB_ARCH_STRING ".sh" # define SW_RESOURCE "/users/aheinecke/development/TrustBridge" TB_ARCH_STRING ".sh" # endif #endif MainWindow::MainWindow(bool trayMode): mTrayMode(trayMode) { createActions(); createTrayIcon(); setupGUI(); resize(900, 600); setMinimumWidth(760); 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) { hide(); 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 { if (mTrayIcon->isAlternative()) { mCurMessage = tr("An updated certificate list is available."); } else { mCurMessage = tr("An updated certificate list is available.") +" " + tr("Click here to install."); } setState(NewListAvailable); showMessage(); loadCertificateList(); } } void MainWindow::handleNewSW(const QString& fileName, const QDateTime& modDate) { if (mTrayIcon->isAlternative()) { mCurMessage = tr("An update for %1 is available.").arg( QApplication::applicationName()); } else { mCurMessage = QString(tr("An update for %1 is available.") + "\n" + tr("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_system_install() || !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 */ /* On linux installDir is <prefix>/bin */ QStringList parameters; installDir.cdUp(); parameters << "--prefix" << installDir.path(); bool sudo_started = false; bool use_sudo = is_admin() && is_system_install(); if (use_sudo) { QStringList sudoPrograms; sudoPrograms << "gksudo" << "kdesudo" << "sudo"; QStringList sudoParams; sudoParams << filePath + " " + parameters.join(" "); foreach (const QString &sProg, sudoPrograms) { qDebug() << "Starting process " << sProg <<" params: " << sudoParams; if (!QProcess::startDetached(sProg, sudoParams)) { continue; } else { sudo_started = true; break; } } } qDebug() << "Starting process " << filePath <<" params: " << parameters; if (!sudo_started && !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) { setState(BeforeDownload); connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)), this, SLOT(handleNewSW(const QString&, const QDateTime&))); } else { setState(DownloadingSW); 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(finished()), this, SLOT(updateCheckSuccess())); 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 */ syslog_error_printf ("Failed to check for updates: %s", message.toUtf8().constData()); 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 TrayIcon(this); mTrayIcon->setContextMenu(mTrayMenu); mTrayIcon->setIcon(trayImg); setWindowIcon(trayImg); mTrayIcon->show(); mTrayIcon->setToolTip(tr("TrustBridge")); connect(mTrayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked())); } QWidget * MainWindow::createInfoWidget() { QWidget *theWidget = new QWidget; QVBoxLayout *infoPanelLayout = new QVBoxLayout; QHBoxLayout *infoHeaderLayout = new QHBoxLayout; QVBoxLayout *infoHeaderTextLayout = new QVBoxLayout; QVBoxLayout *infoCenterLayout = new QVBoxLayout; 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(appVersion); infoHeaderLayout->addLayout(infoHeaderTextLayout); infoHeaderLayout->insertStretch(2, 10); QLabel *textDesc = new QLabel(tr("TrustBridge is a root certificate" " installer for Windows and GNU/Linux.<br/>") + tr("The root certificate lists are managed" " by the German <a href=\"https://www.bsi.bund.de\">" "Federal Office for Information Security (BSI)</a>.<br/><br/>") + 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).<br/><br/>") + tr("TrustBridge is Free Software licensed" " under GNU GPL v2+.<br/><br/>Copyright (C) 2014 by Bundesamt für Sicherheit" " in der Informationstechnik")); textDesc->setTextFormat(Qt::RichText); textDesc->setTextInteractionFlags( Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByMouse); textDesc->setOpenExternalLinks(true); infoCenterLayout->addWidget(infoHeaderSeparator); infoCenterLayout->addWidget(textDesc); infoCenterLayout->insertSpacing(2, 10); infoCenterLayout->insertSpacing(4, 10); infoCenterLayout->insertSpacing(6, 10); infoCenterLayout->insertStretch(8, 10); infoPanelLayout->addLayout(infoHeaderLayout); infoPanelLayout->addLayout(infoCenterLayout); theWidget->setLayout(infoPanelLayout); return theWidget; } QWidget * MainWindow::createUpdatesWidget() { QWidget * theWidget = new QWidget; QVBoxLayout *updatesMainLayout = new QVBoxLayout; /* The header */ QGridLayout *updatesHeaderLayout = new QGridLayout; /* Header 1: Update date and search button */ QHBoxLayout *updateDateAndSearchButton = new QHBoxLayout; mUpdatesHeader = new QLabel("<h2>" + tr("Certificates unchanged")+ "</h2>"); mLastCertUpdate = new QLabel(tr("Installed certificates from:")); mLastCertUpdateContents = new QLabel(QString()); mLastCertUpdate->hide(); mLastCertUpdateContents->hide(); mSoftwareVersionLabel = new QLabel(tr("TrustBridge Version:")); mSoftwareVersionContents = new QLabel(QApplication::applicationVersion()); const QDateTime lastCheck = mSettings.value("lastUpdateCheck").toDateTime().toLocalTime(); if (lastCheck.isValid()) { const QString lastUpdateCheck = QLocale::system().toString(lastCheck, DATETIME_FORMAT); mLastUpdateCheck = new QLabel(tr("Last successful update check:")); mLastUpdateCheckContents = new QLabel(lastUpdateCheck); } else { mLastUpdateCheck = new QLabel(tr("Last successful update check:")); mLastUpdateCheckContents = new QLabel(tr("No connection with the updateserver.")); } QPushButton *searchUpdates = new QPushButton(" " + tr("Update")); searchUpdates->setFixedHeight(22); searchUpdates->setToolTip(tr("Check for Updates")); searchUpdates->setStyleSheet("font-size: 10px;"); searchUpdates->setIcon(QIcon(":/img/update-list.png")); connect(searchUpdates, SIGNAL(clicked()), this, SLOT(checkUpdates())); updateDateAndSearchButton->addWidget(mLastUpdateCheckContents); updateDateAndSearchButton->addWidget(searchUpdates); // addWidget(*Widget, row, column, rowspan, colspan, [Qt::Alignment]) updatesHeaderLayout->addWidget(mUpdatesHeader, 0, 0, 1, 2); updatesHeaderLayout->addWidget(mSoftwareVersionLabel, 1, 0, 1, 1); updatesHeaderLayout->addWidget(mSoftwareVersionContents, 1, 1, 1, 1); updatesHeaderLayout->addWidget(mLastUpdateCheck, 2, 0, 1, 1); updatesHeaderLayout->addLayout(updateDateAndSearchButton, 2, 1, 1, 1); // updatesHeaderLayout->addWidget(mLastUpdateCheckContents, 2, 1, 1, 1); updatesHeaderLayout->addWidget(mLastCertUpdate, 3, 0, 1, 1); updatesHeaderLayout->addWidget(mLastCertUpdateContents, 3, 1, 1, 1); updatesHeaderLayout->setColumnStretch(3, 1); // updatesHeaderLayout->addWidget(searchUpdates, 1, 4, 1, 2, Qt::AlignRight); updatesHeaderLayout->setRowMinimumHeight(4, 15); /* Header 2: Action text and buttons */ mUpdatesTip = new QLabel(tr("There are currently no changes for your certificate stores.")); mUpdatesTip->setWordWrap(true); QHBoxLayout *updatesHeaderActionButtonLayout = new QHBoxLayout; mQuitButton = new QPushButton(" " + tr("Quit without saving")); mQuitButton->setIcon(QIcon(":/img/application-exit.png")); mQuitButton->setFixedHeight(30); mInstallButton = new QPushButton(" " + tr("Install certificates again")); mInstallButton->setFixedHeight(30); #ifdef Q_OS_WIN if (is_admin() && is_system_install()) { QIcon uacShield = QApplication::style()->standardIcon(QStyle::SP_VistaShield); mInstallButton->setIcon(uacShield); } #else mInstallButton->setIcon(QIcon(":/img/view-refresh_16px.png")); #endif connect(mQuitButton, SIGNAL(clicked()), this, SLOT(closeApp())); connect(mInstallButton, SIGNAL(clicked()), this, SLOT(checkAndInstallCerts())); // addWidget(*Widget, row, column, rowspan, colspan) updatesHeaderLayout->addWidget(mUpdatesTip, 5, 0, 1, 4); updatesHeaderActionButtonLayout->addWidget(mInstallButton); updatesHeaderActionButtonLayout->addWidget(mQuitButton); updatesHeaderLayout->addLayout(updatesHeaderActionButtonLayout, 6, 0, 1, -1, Qt::AlignRight); updatesHeaderLayout->setRowMinimumHeight(7, 10); /* The central panels. */ QScrollArea *centralScrollArea = new QScrollArea; 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(); mUpdatesDetailsNew->setText(" " + tr("Details")); mUpdatesDetailsNew->setToolTip(tr("Show details")); mUpdatesDetailsNew->setStyleSheet("font-size: 10px;"); mUpdatesDetailsNew->setFixedHeight(22); mUpdatesDetailsNew->setIcon(QIcon(":/img/dialog-information_16px.png")); connect(mUpdatesDetailsNew, SIGNAL(clicked()), this, SLOT(toggleUpdatesNew())); updatesNewLayout->addWidget(mUpdatesNewCertificates); updatesNewLayout->addWidget(mUpdatesDetailsNew); updatesNewLayout->addStretch(1); 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(); mUpdatesDetailsRemove->setText(" " + tr("Details")); mUpdatesDetailsRemove->setToolTip(tr("Show details")); mUpdatesDetailsRemove->setStyleSheet("font-size: 10px;"); mUpdatesDetailsRemove->setFixedHeight(22); mUpdatesDetailsRemove->setIcon(QIcon(":/img/dialog-information_16px.png")); connect(mUpdatesDetailsRemove, SIGNAL(clicked()), this, SLOT(toggleUpdatesRemove())); updatesRemoveLayout->addWidget(mUpdatesRemoveCertificates); updatesRemoveLayout->addWidget(mUpdatesDetailsRemove); updatesRemoveLayout->addStretch(1); 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(); mUpdatesDetailsManual->setText(" " + tr("Details")); mUpdatesDetailsManual->setToolTip(tr("Show details")); mUpdatesDetailsManual->setStyleSheet("font-size: 10px;"); mUpdatesDetailsManual->setFixedHeight(22); mUpdatesDetailsManual->setIcon(QIcon(":/img/dialog-information_16px.png")); connect(mUpdatesDetailsManual, SIGNAL(clicked()), this, SLOT(toggleUpdatesManual())); mUpdatesDetailsManual->hide(); mUpdatesManualCertificates->hide(); updatesManualLayout->addWidget(mUpdatesManualCertificates); updatesManualLayout->addWidget(mUpdatesDetailsManual); updatesManualLayout->addStretch(1); mUpdatesManual = new CertificateListWidget(this); mUpdatesManual->hide(); connect(mUpdatesManual, SIGNAL(certChanged(bool, const Certificate&)), this, SLOT(removeFromManual(bool, const Certificate&))); connect(mUpdatesManual, SIGNAL(certListChanged(int)), this, SLOT(listChanged(int))); 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); QWidget *dummyWidget = new QWidget; dummyWidget->setLayout(updatesCenterLayout); centralScrollArea->setWidgetResizable(true); centralScrollArea->setWidget(dummyWidget); centralScrollArea->setFrameShape(QFrame::NoFrame); centralScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); updatesMainLayout->addLayout(updatesHeaderLayout); updatesMainLayout->addWidget(centralScrollArea); updatesCenterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); theWidget->setLayout(updatesMainLayout); return theWidget; } QWidget *MainWindow::createInstallWidget() { QWidget *theWidget = new QWidget; QScrollArea *scrollArea = 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.")); installHeaderText->setWordWrap(true); 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.")); installCenterText->setWordWrap(true); installCenterLayout->addWidget(installCenterText); installPanelLayout->addLayout(installHeaderLayout); installPanelLayout->addLayout(installCenterLayout); mInstallList = new CertificateListWidget(this); connect(mInstallList, SIGNAL(certChanged(bool, const Certificate&)), this, SLOT(toggleInManual(bool, const Certificate&))); scrollArea->setWidgetResizable(true); scrollArea->setWidget(mInstallList); scrollArea->setFrameShape(QFrame::NoFrame); installPanelLayout->addWidget(scrollArea); theWidget->setLayout(installPanelLayout); return theWidget; } QWidget *MainWindow::createRemoveWidget() { QWidget * theWidget = new QWidget; QScrollArea *scrollArea = 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.")); removeHeaderText->setWordWrap(true); removeHeaderLayout->addWidget(removeHeaderLabel); removeHeaderLayout->addWidget(removeHeaderText); QLabel *removeCenterText = new QLabel(tr("The following 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.")); removeCenterText->setWordWrap(true); 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); scrollArea->setWidgetResizable(true); scrollArea->setWidget(mRemoveList); scrollArea->setFrameShape(QFrame::NoFrame); removePanelLayout->addWidget(scrollArea); theWidget->setLayout(removePanelLayout); return theWidget; } void MainWindow::setupGUI() { // 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(tr("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; TextOverlayButton *updatesButton = new TextOverlayButton; updatesButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); updatesButton->setIcon(QIcon(":/img/view-refresh.png")); updatesButton->setIconSize(QSize(48, 48)); updatesButton->setText(tr("Updates")); updatesButton->setFixedWidth(120); updatesButton->setFixedHeight(90); updatesButton->setCheckable(true); updatesButton->setChecked(true); connect(this, SIGNAL(changesChanged(const QString&)), updatesButton, SLOT(setOverlay(const QString&))); QToolButton *allInstallButton = new QToolButton; allInstallButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); allInstallButton->setIcon(QIcon(":/img/document-encrypt.png")); allInstallButton->setIconSize(QSize(48, 48)); allInstallButton->setText(tr("Trusted\ncertificates")); allInstallButton->setFixedWidth(120); allInstallButton->setFixedHeight(90); allInstallButton->setCheckable(true); QToolButton *allRemoveButton = new QToolButton; allRemoveButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); allRemoveButton->setIcon(QIcon(":/img/dialog-warning.png")); allRemoveButton->setIconSize(QSize(48, 48)); allRemoveButton->setText(tr("Revoked\ncertificates")); allRemoveButton->setFixedWidth(120); allRemoveButton->setFixedHeight(90); allRemoveButton->setCheckable(true); QToolButton *infoButton = new QToolButton; infoButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); infoButton->setIcon(QIcon(":/img/dialog-information.png")); infoButton->setIconSize(QSize(48, 48)); infoButton->setText(tr("Information\nand help")); infoButton->setFixedWidth(120); infoButton->setFixedHeight(90); 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. */ mUpdatesWidget = createUpdatesWidget(); /* Install (trusted certs) Page */ mInstallWidget = createInstallWidget(); /********************************** * Page for certificates to be removed. **********************************/ mRemoveWidget = createRemoveWidget(); /********************************** * The info page. **********************************/ mInfoWidget = createInfoWidget(); /******************************** * The main layout for pages. ********************************/ mInstallWidget->hide(); mRemoveWidget->hide(); mInfoWidget->hide(); containerLayout->addWidget(mUpdatesWidget); containerLayout->addWidget(mInstallWidget); containerLayout->addWidget(mRemoveWidget); containerLayout->addWidget(mInfoWidget); 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) { Q_UNUSED (selected); setChangeCount(mUpdatesRemove->selectedCertCount() + mUpdatesNew->selectedCertCount() + mUpdatesManual->certificates().size()); /* Show a different tip in case of manual changes, updates aviailable, updates and manual * changes available */ if (changeCount() && !mUpdatesManual->certificates().size()) { mUpdatesTip->setText( tr("You should apply the following, recommended changes to your certificate stores.")); } else if (changeCount()) { mUpdatesTip->setText( tr("You can apply the following, changes to your certificate stores.")); } else { mUpdatesTip->setText( tr("There are currently no changes for your certificate stores.")); } if (!changeCount()) { /* No changes */ mQuitButton->setText(" " + tr("Quit")); mUpdatesHeader->setText("<h2>" + tr("Certificates unchanged") + "</h2>"); mInstallButton->setText(" " + tr("Install certificates again")); } else { mQuitButton->setText(" " + tr("Quit without saving")); mUpdatesHeader->setText("<h2>" + tr("Changes to certificate stores (%1)") .arg(changeCount()) + "</h2>"); mInstallButton->setText(" " + tr("Apply changes")); } if (mUpdatesManual->certificates().size()) { mUpdatesDetailsManual->show(); mUpdatesDetailsManual->setIcon(QIcon(":/img/dialog-information_16px.png")); mUpdatesDetailsManual->setToolTip(tr("Show details")); mUpdatesManualCertificates->show(); } else { mUpdatesDetailsManual->hide(); mUpdatesManualCertificates->hide(); mUpdatesManual->hide(); } if (mUpdatesNew->certificates().size()) { mUpdatesNewCertificates->setText("<h3>" + tr("Install new trusted certificates (%1/%2)") .arg(mUpdatesNew->selectedCertCount()) .arg(mUpdatesNew->certificates().size()) + "</h3>"); mUpdatesDetailsNew->show(); mUpdatesDetailsNew->setIcon(QIcon(":/img/dialog-information_16px.png")); mUpdatesDetailsNew->setToolTip(tr("Show details")); mUpdatesNewCertificates->show(); } else { mUpdatesDetailsNew->hide(); mUpdatesNew->hide(); mUpdatesNewCertificates->hide(); } if (mUpdatesRemove->certificates().size()) { mUpdatesRemoveCertificates->setText("<h3>" + tr("Remove revoked certificates (%1/%2)") .arg(mUpdatesRemove->selectedCertCount()) .arg(mUpdatesRemove->certificates().size()) + "</h3>"); mUpdatesRemoveCertificates->show(); mUpdatesDetailsRemove->setIcon(QIcon(":/img/dialog-information_16px.png")); mUpdatesDetailsRemove->setToolTip(tr("Show details")); mUpdatesDetailsRemove->show(); } else { mUpdatesRemoveCertificates->hide(); mUpdatesDetailsRemove->hide(); mUpdatesRemove->hide(); } } void MainWindow::loadCertificateList() { /* TODO: if nothing is available (neither old nor new) add some progress * indication */ mInstallList->clear(); mRemoveList->clear(); mUpdatesNew->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, true, tr("install"), tr("ignore")); } } 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); } } } } if (mInstalledList.date().isValid()) { mLastCertUpdateContents->setText(QLocale::system().toString(mInstalledList.date().toLocalTime(), DATETIME_FORMAT)); mLastCertUpdate->show(); mLastCertUpdateContents->show(); } mUpdatesManualCertificates->setText("<h3>" + tr("Manually changed certificates (%1)").arg(0) + "</h3>"); listChanged(0); } void MainWindow::installerError(const QString& errMsg) { QMessageBox::warning(this, tr("Error executing update"), errMsg); } void MainWindow::installerSuccess() { if (mCurState == NewListAvailable || mCurState == NewSoftwareAvailable) { 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 */ } /* Rename the installed list to list-installed.txt so that external * programs (like the uninstaller can easily recognize it). */ QString dataLoc = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QDir dataDir(dataLoc); if (!dataDir.exists()) { /* Should not happen */ qWarning() << "Data dir removed."; return; } QFileInfo oldList (dataDir.absoluteFilePath("list-installed.txt")); if (oldList.exists()) { qDebug() << "Removing old list: " << oldList.filePath(); if (!QFile::remove(oldList.filePath())) { qWarning() << "Removal of old list failed."; return; } } QFile newList(listFileName); if (!newList.rename(oldList.filePath())) { qWarning() << "Failed to rename new list."; return; } mSettings.setValue("List/installed", oldList.filePath()); mSettings.setValue("List/installedDate", listFileDate); mInstalledList = CertificateList(oldList.filePath().toUtf8().constData()); if (!mInstalledList.isValid()) { /* Something went wrong. Go back to square one. */ qWarning () << "List corrupted after installation"; mInstalledList = CertificateList(); QFile::remove(oldList.filePath()); mSettings.remove("List/installed"); mSettings.remove("List/installedDate"); } mListToInstall = CertificateList(); } mUpdatesManual->clear(); loadCertificateList(); } void MainWindow::installCerts() { QStringList choices; QStringList unselected; choices << mUpdatesNew->selectedCertificates(); choices << mUpdatesRemove->selectedCertificates(); choices << mUpdatesManual->selectedCertificates(); /* Also include the old certificates */ choices << mInstallList->selectedCertificates(); choices << mRemoveList->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(); #ifdef Q_OS_WIN if (!is_admin() || !is_system_install()) { QMessageBox::information(this, tr("Installation with standard user account"), tr("Windows will now ask you to confirm qeach root certificate modification " "because TrustBridge does not have the necessary privileges to install " "root certificates into the Windows certificate store silently.")); } #endif 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() { ProcessHelp::cleanUp(); 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) { mUpdatesWidget->hide(); mInstallWidget->hide(); mRemoveWidget->hide(); mInfoWidget->hide(); switch(button) { case 0: mUpdatesWidget->show(); break; case 1: mInstallWidget->show(); break; case 2: mRemoveWidget->show(); break; case 3: mInfoWidget->show(); break; default: mUpdatesWidget->show(); break; } return; } void MainWindow::toggleUpdatesNew() { if (!mUpdatesNew->isVisible()) { mUpdatesDetailsNew->setToolTip(tr("Hide details")); mUpdatesDetailsNew->setIcon(QIcon(":/img/dialog-information_grey_16px.png")); mUpdatesNew->show(); } else { mUpdatesNew->hide(); mUpdatesDetailsNew->setToolTip(tr("Show details")); mUpdatesDetailsNew->setIcon(QIcon(":/img/dialog-information_16px.png")); } } void MainWindow::toggleUpdatesRemove() { if (!mUpdatesRemove->isVisible()) { mUpdatesDetailsRemove->setToolTip(tr("Hide details")); mUpdatesDetailsRemove->setIcon(QIcon(":/img/dialog-information_grey_16px.png")); mUpdatesRemove->show(); } else { mUpdatesRemove->hide(); mUpdatesDetailsRemove->setToolTip(tr("Show details")); mUpdatesDetailsRemove->setIcon(QIcon(":/img/dialog-information_16px.png")); } } void MainWindow::toggleUpdatesManual() { if (!mUpdatesManual->isVisible()) { mUpdatesDetailsManual->setToolTip(tr("Hide details")); mUpdatesDetailsManual->setIcon(QIcon(":/img/dialog-information_grey_16px.png")); mUpdatesManual->show(); } else { mUpdatesDetailsManual->setToolTip(tr("Show details")); mUpdatesDetailsManual->setIcon(QIcon(":/img/dialog-information_16px.png")); mUpdatesManual->hide(); } } void MainWindow::closeEvent(QCloseEvent *event) { if (getState() == NewListAvailable) { /* Only minimize to tray if there is a new list */ QMainWindow::closeEvent(event); return; } return closeApp(); } void MainWindow::updateCheckSuccess() { if (getState() != TransferError) { const QDateTime now = QDateTime::currentDateTime(); mSettings.setValue("lastUpdateCheck", now); mLastUpdateCheckContents->setText(QLocale::system().toString(now, DATETIME_FORMAT)); mLastUpdateCheckContents->show(); mLastUpdateCheck->show(); syslog_info_printf(tr("Sucessfully checked for updates.").toUtf8().constData()); } } int MainWindow::changeCount() { return mChangeCount; } void MainWindow::setChangeCount(int cnt) { if (mChangeCount != cnt) { mChangeCount = cnt; emit changesChanged(QString("%1").arg(cnt)); } }