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:  */
rrenkert@333: #include "administratorwindow.h"
rrenkert@333: 
rrenkert@333: #include <QDebug>
rrenkert@333: #include <QMessageBox>
rrenkert@333: #include <QAction>
rrenkert@333: #include <QMenu>
rrenkert@333: #include <QApplication>
rrenkert@333: #include <QHBoxLayout>
rrenkert@333: #include <QVBoxLayout>
rrenkert@333: #include <QGroupBox>
rrenkert@333: #include <QSplitter>
rrenkert@333: #include <QLabel>
rrenkert@333: #include <QImage>
rrenkert@333: #include <QCheckBox>
rrenkert@335: #include <QHeaderView>
rrenkert@344: #include <QFileDialog>
rrenkert@394: #include <QStandardPaths>
rrenkert@398: #include <QSortFilterProxyModel>
rrenkert@333: 
rrenkert@348: #include "certificatetabledelegate.h"
rrenkert@362: #include "createinstallerdialog.h"
rrenkert@368: #include "createcertlistdialog.h"
rrenkert@566: #include "certificatediffdialog.h"
rrenkert@427: #include "aboutdialog.h"
rrenkert@348: 
rrenkert@333: AdministratorWindow::AdministratorWindow() {
rrenkert@412:     setWindowTitle(tr("TrustBridge Administration"));
rrenkert@394:     QString path = QStandardPaths::locate(
aheinecke@462:         QStandardPaths::DataLocation, QString("current_certificates.txt"));
aheinecke@453:     mCertList.readList(path.toLocal8Bit());
rrenkert@333:     createActions();
rrenkert@333:     createMenuBar();
rrenkert@333:     createContent();
rrenkert@394:     loadCertificateTable();
aheinecke@540:     resize(1190, 500);
rrenkert@333: }
rrenkert@333: 
rrenkert@333: void AdministratorWindow::createActions()
rrenkert@333: {
rrenkert@333: }
rrenkert@333: 
rrenkert@333: void AdministratorWindow::createMenuBar()
rrenkert@333: {
rrenkert@333:     menuBar = new QMenuBar(this);
rrenkert@333:     QMenu *menu = new QMenu(tr("Menu"), menuBar);
rrenkert@333:     menuBar->addMenu(menu);
rrenkert@415:     QAction *createInstaller = menu->addAction(tr("Create installer ..."));
rrenkert@415:     QAction *about = menu->addAction(tr("About TrustBridge"));
rrenkert@333:     menu->addSeparator();
rrenkert@333:     QAction *quit = menu->addAction(tr("Quit"));
rrenkert@333:     connect(createInstaller, SIGNAL(triggered()), this, SLOT(createInstaller()));
rrenkert@333:     connect(about, SIGNAL(triggered()), this, SLOT(showAbout()));
rrenkert@333:     connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
rrenkert@333:     setMenuBar(menuBar);
rrenkert@333: }
rrenkert@333: 
rrenkert@333: void AdministratorWindow::createContent()
rrenkert@333: {
rrenkert@333:     // Create a central widget containing the main layout.
rrenkert@333:     QWidget *base = new QWidget;
rrenkert@333: 
rrenkert@333:     // Layouts and Container
rrenkert@333:     QVBoxLayout *mainLayout = new QVBoxLayout;
rrenkert@333:     QVBoxLayout *certLayout = new QVBoxLayout;
rrenkert@333:     QHBoxLayout *headerLayout = new QHBoxLayout;
rrenkert@333:     QVBoxLayout *headerTextLayout = new QVBoxLayout;
rrenkert@333:     QHBoxLayout *bottomLayout = new QHBoxLayout;
rrenkert@333: 
rrenkert@333:     // The certificate list
rrenkert@412:     QGroupBox *certBox = new QGroupBox(
rrenkert@426:         tr("All managed root certificates of the certificate list: "));
rrenkert@335:     certificateView = new QTableView;
rrenkert@343:     certificateModel = new CertificateTabelModel();
rrenkert@398:     QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(this);
rrenkert@398:     filterModel->setSourceModel(certificateModel);
rrenkert@348:     CertificateTableDelegate *delegate = new CertificateTableDelegate(certificateView);
rrenkert@398:     certificateView->setModel(filterModel);
rrenkert@348:     certificateView->setItemDelegate(delegate);
rrenkert@426:     certificateView->horizontalHeader()->setStretchLastSection(true);
rrenkert@395:     certificateView->resizeColumnsToContents();
rrenkert@395:     certificateView->setColumnWidth(0, 60);
rrenkert@343:     certificateView->setSelectionBehavior(QAbstractItemView::SelectRows);
rrenkert@376:     connect(certificateView, SIGNAL(clicked(const QModelIndex&)), this,
rrenkert@376:         SLOT(clickedCertificate(const QModelIndex&)));
rrenkert@343:     certificateView->verticalHeader()->setVisible(false);
rrenkert@398:     certificateView->setSortingEnabled(true);
rrenkert@335:     certLayout->addWidget(certificateView);
rrenkert@333:     certBox->setLayout(certLayout);
rrenkert@333: 
rrenkert@333:     // The header (icon, about text)
rrenkert@333:     QImage *logoImage = new QImage(":/img/logo.png");
rrenkert@333:     QLabel *logo = new QLabel;
rrenkert@333:     logo->setBackgroundRole(QPalette::Base);
rrenkert@333:     logo->setPixmap(QPixmap::fromImage(*logoImage));
rrenkert@427:     QLabel *title = new QLabel("<h2>" + tr("TrustBridge Administration") + "</h2>");
rrenkert@412:     QLabel *subTitle = new QLabel(
rrenkert@412:         tr("Management application of the BSI certificate installer"));
rrenkert@333:     headerTextLayout->addWidget(title);
rrenkert@333:     headerTextLayout->addWidget(subTitle);
rrenkert@333:     headerLayout->addWidget(logo);
rrenkert@333:     headerLayout->addLayout(headerTextLayout);
rrenkert@333:     headerLayout->setStretch(0, 0);
rrenkert@333:     headerLayout->setStretch(1, 10);
rrenkert@333: 
rrenkert@333:     // The buttons.
rrenkert@333:     bottomLayout->setAlignment(Qt::AlignBottom);
rrenkert@412:     saveButton = new QPushButton(QIcon(":/img/document-save.png"), tr("Save list"));
rrenkert@368:     connect(saveButton, SIGNAL(clicked()), this, SLOT(saveCertificateFile()));
rrenkert@412:     loadButton = new QPushButton(QIcon(":/img/document-open.png"), tr("Load list"));
rrenkert@344:     connect(loadButton, SIGNAL(clicked()), this, SLOT(loadCertificateFile()));
rrenkert@412:     addButton = new QPushButton(QIcon(":/img/document-new.png"), tr("Add certificate"));
rrenkert@358:     connect(addButton, SIGNAL(clicked()), this, SLOT(addCertificates()));
rrenkert@412:     removeButton = new QPushButton(QIcon(":/img/document-close.png"), tr("Remove certificate"));
rrenkert@376:     removeButton->setEnabled(false);
rrenkert@376:     connect(removeButton, SIGNAL(clicked()), this, SLOT(removeCertificates()));
rrenkert@333:     bottomLayout->addWidget(saveButton);
rrenkert@333:     bottomLayout->addWidget(loadButton);
rrenkert@333:     bottomLayout->addWidget(addButton);
rrenkert@333:     bottomLayout->addWidget(removeButton);
rrenkert@333:     bottomLayout->insertStretch(4, 10);
rrenkert@333: 
rrenkert@333:     mainLayout->addLayout(headerLayout);
rrenkert@333:     mainLayout->addWidget(certBox);
rrenkert@333:     mainLayout->addLayout(bottomLayout);
rrenkert@333: 
rrenkert@333: 
rrenkert@333:     // QMainWindow allready has a layout. All child layouts and widgets are
rrenkert@333:     // managed in the central widget.
rrenkert@333:     base->setLayout(mainLayout);
rrenkert@333:     setCentralWidget(base);
rrenkert@333: }
rrenkert@333: 
rrenkert@344: void AdministratorWindow::loadCertificateFile()
rrenkert@344: {
aheinecke@515:     QString lastCertFile = mSettings.value("LastCertList", QDir::homePath()).toString();
rrenkert@344:     QString certFile = QFileDialog::getOpenFileName(
aheinecke@515:         this, tr("Select certificate list file"), lastCertFile, "*.txt");
rrenkert@567:     if (certFile.isNull()) {
rrenkert@567:         return;
rrenkert@567:     }
aheinecke@453:     mCertList.readList(certFile.toLocal8Bit().constData());
aheinecke@453:     if (!mCertList.isValid()) {
aheinecke@515:         QMessageBox::warning(this, tr("Error!"), tr("Failed to load the certificate list."));
aheinecke@462:     } else {
rrenkert@396:         certificateModel->removeAll();
rrenkert@344:         loadCertificateTable();
aheinecke@515:         mSettings.setValue("LastCertList", certFile);
rrenkert@344:     }
rrenkert@344: }
rrenkert@344: 
rrenkert@368: void AdministratorWindow::saveCertificateFile()
rrenkert@368: {
rrenkert@566:     CertificateDiffDialog *diffDialog = new CertificateDiffDialog(this);
rrenkert@566:     int ret = diffDialog->exec();
rrenkert@566:     if (ret == QDialog::Accepted) {
rrenkert@566:         CreateCertListDialog *dialog = new CreateCertListDialog(this);
rrenkert@566:         dialog->show();
rrenkert@566:     }
rrenkert@368: }
rrenkert@368: 
rrenkert@358: void AdministratorWindow::addCertificates()
rrenkert@358: {
rrenkert@429:     QStringList certFiles = QFileDialog::getOpenFileNames(
aheinecke@529:         this, tr("Select certificate"), QDir::homePath(), "*.pem *.der *.crt *.cer");
rrenkert@429:     for (int i = 0; i < certFiles.size(); i++) {
rrenkert@429:         QString certFile = certFiles.at(i);
rrenkert@429:         QList<Certificate> certs = Certificate::fromFileName(certFile);
rrenkert@429:         addToCertificateTable(certs);
rrenkert@429:     }
rrenkert@427:     certificateView->resizeColumnsToContents();
rrenkert@427:     certificateView->setColumnWidth(0, 60);
rrenkert@358: }
rrenkert@358: 
rrenkert@376: void AdministratorWindow::removeCertificates()
rrenkert@376: {
rrenkert@376:     QModelIndexList list = certificateView->selectionModel()->selectedRows();
rrenkert@376:     for (int i = list.size() -1 ; i >= 0; i--) {
rrenkert@376:         certificateModel->removeRow(list.at(i).row(), list.at(i));
rrenkert@376:     }
rrenkert@376: }
rrenkert@376: 
rrenkert@344: void AdministratorWindow::loadCertificateTable() {
aheinecke@453:     foreach(const Certificate &cert, mCertList.getCertificates()) {
rrenkert@358:         certificateModel->addCertificate(cert, true);
rrenkert@358:     }
rrenkert@395:     certificateView->resizeColumnsToContents();
rrenkert@395:     certificateView->setColumnWidth(0, 60);
rrenkert@358: }
rrenkert@358: 
rrenkert@358: void AdministratorWindow::addToCertificateTable(const QList<Certificate> &certs)
rrenkert@358: {
rrenkert@358:     foreach(const Certificate &cert, certs) {
rrenkert@358:         certificateModel->addCertificate(cert, false);
rrenkert@344:     }
rrenkert@344: }
rrenkert@344: 
rrenkert@333: void AdministratorWindow::showAbout()
rrenkert@333: {
rrenkert@427:     AboutDialog *dialog = new AboutDialog(this);
rrenkert@427:     dialog->show();
rrenkert@333: }
rrenkert@333: 
rrenkert@333: void AdministratorWindow::createInstaller()
rrenkert@333: {
rrenkert@333:     qDebug() << "create Installer";
rrenkert@362:     CreateInstallerDialog *dialog = new CreateInstallerDialog(this);
rrenkert@362:     dialog->show();
rrenkert@333: }
rrenkert@333: 
rrenkert@376: void AdministratorWindow::clickedCertificate(const QModelIndex &index)
rrenkert@376: {
rrenkert@376:     if (index.data(Qt::UserRole).toBool()) {
rrenkert@376:         removeButton->setEnabled(false);
rrenkert@376:     }
rrenkert@376:     else {
rrenkert@376:         removeButton->setEnabled(true);
rrenkert@376:     }
rrenkert@376: }
rrenkert@562: 
andre@679: void AdministratorWindow::logChanges(const QString &currentCerts, const QString& keyFingerprint)
rrenkert@562: {
rrenkert@562:     QDir logDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
rrenkert@562:     QString logFilePath = logDir.filePath("log.txt");
rrenkert@562:     QFile logFile(logFilePath);
rrenkert@562: 
rrenkert@562:     if (!logFile.open(QIODevice::Append)) {
aheinecke@570:         QMessageBox::warning(this, tr("Error!"), tr("Failed to open log file: ") +
aheinecke@570:                 logFilePath + tr(" Changes are not logged!"));
rrenkert@562:         qDebug() << "Failed to open log file: " << logFilePath;
rrenkert@562:         return;
rrenkert@562:     }
rrenkert@562: 
rrenkert@562:     CertificateList newCertList;
rrenkert@562:     newCertList.readList(currentCerts.toLocal8Bit());
andre@679:     QByteArray entries = createLogEntries(newCertList, keyFingerprint);
rrenkert@562:     if(logFile.write(entries) != entries.size()) {
aheinecke@570:         QMessageBox::warning(this, tr("Error!"), tr("Failed to write log file: ") +
aheinecke@570:                 logFilePath + tr(" Changes are not logged!"));
rrenkert@562:         return;
rrenkert@562:     }
rrenkert@562:     logFile.close();
rrenkert@562: }
rrenkert@562: 
andre@679: QByteArray AdministratorWindow::createLogEntries(const CertificateList &list, const QString& keyFingerprint)
rrenkert@562: {
rrenkert@562:     QByteArray entries;
rrenkert@562:     QByteArray removeListEntries;
rrenkert@562: 
rrenkert@562:     QDateTime currentDate = QDateTime::currentDateTime();
rrenkert@562:     QDateTime newListDate = list.date();
rrenkert@562:     QDateTime listDate = mCertList.date();
rrenkert@562: 
rrenkert@562:     entries.append("##### " +
rrenkert@562:         currentDate.toString("yyyy-MM-dd hh:mm") +
rrenkert@562:         tr(" new certificatelist ") +
rrenkert@562:         newListDate.toString(Qt::ISODate) +
rrenkert@562:         tr(" based on list from ") +
rrenkert@562:         listDate.toString(Qt::ISODate) +
rrenkert@562:         "#####\r\n");
rrenkert@562:     entries.append(tr("signing certificate: \r\n"));
andre@679:     entries.append(keyFingerprint);
andre@679: 
andre@679:     entries.append(tr("\r\nnew certificates:\r\n"));
rrenkert@562: 
rrenkert@562:     foreach (const Certificate& cert, list.getCertificates()) {
rrenkert@562:         if (!mCertList.getCertificates().contains(cert)) {
rrenkert@562:             QString certEntry(cert.subjectCN() + ": " + cert.base64Line() + "\r\n");
rrenkert@562:             if (cert.isInstallCert()) {
rrenkert@562:                 entries.append(certEntry);
rrenkert@562:             }
rrenkert@562:             else {
rrenkert@562:                 removeListEntries.append(certEntry);
rrenkert@562:             }
rrenkert@562:         }
rrenkert@562:     }
rrenkert@562: 
rrenkert@562:     entries.append(tr("certificates marked to remove:\r\n"));
rrenkert@562:     entries.append(removeListEntries);
rrenkert@562:     entries.append("\r\n");
rrenkert@562: 
rrenkert@562:     return entries;
rrenkert@562: }
rrenkert@565: 
rrenkert@565: QList<Certificate> AdministratorWindow::currentChanges()
rrenkert@565: {
rrenkert@565:     QList<Certificate> changed;
rrenkert@565:     foreach(const Certificate& cert, certificates()) {
rrenkert@565:         if (!mCertList.getCertificates().contains(cert)) {
rrenkert@565:             changed.append(cert);
rrenkert@565:         }
rrenkert@565:     }
rrenkert@565:     return changed;
rrenkert@565: }