changeset 369:78eec57bc133

merged.
author Raimund Renkert <rrenkert@intevation.de>
date Tue, 15 Apr 2014 09:44:54 +0200
parents f9c98f9e9f76 (current diff) c0d63a7f270e (diff)
children 8b9ba61add57 9731d28b95af
files ui/CMakeLists.txt ui/listupdatedialog.cpp ui/listupdatedialog.h
diffstat 11 files changed, 190 insertions(+), 297 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Apr 15 09:44:32 2014 +0200
+++ b/CMakeLists.txt	Tue Apr 15 09:44:54 2014 +0200
@@ -54,6 +54,12 @@
    set(PROFILING_LIBS gcov)
 endif()
 
+if(WIN32)
+   add_definitions(-D_WIN32_WINNT=0x0600) # Windows vista
+   add_definitions(-DWINVER=0x0600)
+   add_definitions(-DMINGW_HAS_SECURE_API) # for _s functions
+endif(WIN32)
+
 if(UNIX)
    set(HARDENING_FLAGS " ${HARDENING_FLAGS} -pie -fPIE -ftrapv")
    set(HARDENING_FLAGS " ${HARDENING_FLAGS} -Wl,-z,relro,-z,now")
--- a/cinst/CMakeLists.txt	Tue Apr 15 09:44:32 2014 +0200
+++ b/cinst/CMakeLists.txt	Tue Apr 15 09:44:54 2014 +0200
@@ -13,7 +13,7 @@
 add_executable(cinst ${CINST_SOURCES})
 
 if (WIN32)
-   set(WIN_EXTRA_LIBS -lcrypt32 -luserenv)
+   set(WIN_EXTRA_LIBS -lcrypt32 -luserenv -lshell32)
 endif(WIN32)
 
 target_link_libraries(cinst
--- a/cinst/nssstore_win.c	Tue Apr 15 09:44:32 2014 +0200
+++ b/cinst/nssstore_win.c	Tue Apr 15 09:44:54 2014 +0200
@@ -158,8 +158,8 @@
   cmd_line_len = wcslen (lpApplicationName) + wcslen(selection_file) + 1;
   lpCommandLine = xmalloc (cmd_line_len * sizeof(wchar_t));
 
-  wcscpy (lpCommandLine, lpApplicationName);
-  wcscat (lpCommandLine, selection_file);
+  wcscpy_s (lpCommandLine, cmd_line_len, lpApplicationName);
+  wcscat_s (lpCommandLine, cmd_line_len, selection_file);
 
   success = CreateProcessAsUserW (hToken,
                                   lpApplicationName,
@@ -201,7 +201,7 @@
   * This creates a security attributes structure that restricts
   * write access to the Administrators group but allows everyone to read files
   * in that directory.
-  * Basically a very complicated vetrsion of mkdir path -m 604
+  * Basically a very complicated version of mkdir path -m 644
   *
   * If the directory exists the permissions of that directory are checked if
   * they are acceptable and true or false is returned accordingly.
@@ -365,16 +365,11 @@
       /* TODO */
     }
 
-#if 0
-  result = SHGetKnownFolderPath (FOLDERID_ProgramData, /* Get program data dir */
+  result = SHGetKnownFolderPath (&FOLDERID_ProgramData, /* Get program data dir */
                                  KF_FLAG_CREATE | /* Create if it does not exist */
                                  KF_FLAG_INIT, /* Initialize it if created */
-                                 -1, /* Get it for the default user */
+                                 INVALID_HANDLE_VALUE, /* Get it for the default user */
                                  &folder_name);
-#endif
-  /* TODO figure out how to do this with mingw */
-  result = S_OK;
-  folder_name = L"C:\\ProgramData";
 
   if (result != S_OK)
     {
@@ -394,12 +389,12 @@
     }
 
   path = xmalloc (path_len * sizeof (wchar_t));
-  if (wcscpy (path, folder_name) != 0)
+  if (wcscpy_s (path, path_len, folder_name) != 0)
     {
       ERRORPRINTF ("Failed to copy folder name.\n");
-#if 0
+
       CoTaskMemFree (folder_name);
-#endif
+
       return NULL;
     }
 
@@ -407,14 +402,14 @@
   CoTaskMemFree (folder_name);
 #endif
 
-  if (wcscat (path, L"\\") != 0)
+  if (wcscat_s (path, path_len, L"\\") != 0)
     {
       ERRORPRINTF ("Failed to cat dirsep.\n");
       xfree(path);
       return NULL;
     }
 
-  if (wcscat (path, APPNAME) != 0)
+  if (wcscat_s (path, path_len, APPNAME) != 0)
     {
       ERRORPRINTF ("Failed to cat appname.\n");
       xfree(path);
@@ -432,14 +427,14 @@
       return NULL;
     }
 
-  if (wcscat (path, L"\\") != 0)
+  if (wcscat_s (path, path_len, L"\\") != 0)
     {
       ERRORPRINTF ("Failed to cat dirsep.\n");
       xfree(path);
       return NULL;
     }
 
-  if (wcscat (path, SELECTION_FILE_NAME) != 0)
+  if (wcscat_s (path, path_len, SELECTION_FILE_NAME) != 0)
     {
       ERRORPRINTF ("Failed to cat filename.\n");
       xfree(path);
--- a/ui/CMakeLists.txt	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/CMakeLists.txt	Tue Apr 15 09:44:54 2014 +0200
@@ -16,7 +16,6 @@
 set(M13UI_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/listupdatedialog.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/helpdialog.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/aboutdialog.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/statusdialog.cpp
--- a/ui/installwrapper.cpp	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/installwrapper.cpp	Tue Apr 15 09:44:54 2014 +0200
@@ -1,6 +1,7 @@
 #include "installwrapper.h"
 
 #include <QFileInfo>
+#include <QProcess>
 #include <QTemporaryFile>
 #include <QApplication>
 #include <QDir>
@@ -11,10 +12,10 @@
 #define INSTALL_TIMEOUT 3600000 /* Wait up to an hour */
 
 InstallWrapper::InstallWrapper(QObject* parent,
-        const QString& path, const QStringList& instructions):
+        const QString& path, const QStringList& choices):
     QThread(parent),
     mCertListFile(path),
-    mInstructions(instructions)
+    mChoices(choices)
 {
 }
 
@@ -28,16 +29,32 @@
     return QFileInfo(myDir.absoluteFilePath(instProcName));
 }
 
-#ifdef WIN32
+bool InstallWrapper::writeChoices(QTemporaryFile* choicesFile) const
+{
+    if (!choicesFile->open()) {
+        return false;
+    }
+
+    foreach (const QString &b64data, mChoices) {
+       if (!choicesFile->write(b64data.toLatin1())) {
+           return false;
+       }
+       if (!choicesFile->write("\n")) {
+           return false;
+       }
+    }
+
+    choicesFile->close();
+
+    return true;
+}
+
 void InstallWrapper::run()
 {
     /* TODO: We need errorcodes here so that we can see if a user
      * cancled the UAC elevation */
-    QTemporaryFile instructionsFile;
+    QTemporaryFile choicesFile;
     QFileInfo cinstProcInfo = getCinstProcInfo();
-    DWORD retval = 0;
-    SHELLEXECUTEINFOW shExecInfo;
-    memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOW));
 
     QString cinstFileName = QDir::toNativeSeparators(
             getCinstProcInfo().absoluteFilePath());
@@ -47,17 +64,26 @@
         return;
     }
 
-    instructionsFile.open();
-
-    foreach (const QString &b64data, mInstructions) {
-       instructionsFile.write(b64data.toLatin1());
-       instructionsFile.write("\n");
+    if (!writeChoices(&choicesFile)) {
+        emit error(tr("Failed to write temporary file."));
+        return;
     }
 
-    instructionsFile.close();
+    QString parameters = "\"list=" + mCertListFile +
+        "\" \"choices=" + choicesFile.fileName() + "\"";
 
-    QString parameters = "\"list=" + mCertListFile + 
-        "\" \"instructions=" +instructionsFile.fileName() + "\"";
+#ifdef WIN32
+    /* QProcess on Windows uses CreateProcess but we have to
+     * use the runas shell command to get the UAC prompt if necessary.
+     * So we have to handle the process ourself. Starting with
+     * shell execute also means that we can not have stdout and stderr
+     * redirection. This is the reason we use command line parameters
+     * and not a pipe for communication. In debug mode the installer
+     * also makes use of output debug string. */
+    DWORD retval = 0;
+    SHELLEXECUTEINFOW shExecInfo;
+
+    memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOW));
 
     shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
     shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
@@ -106,9 +132,35 @@
         /* TODO make this nicer */
         emit error (tr("The process failed with return code. %1").arg(retval));
     }
+    return;
+#else /* WIN32 */
+    QProcess installerProcess;
+    installerProcess.setProgram(cinstProcInfo.absoluteFilePath());
+    installerProcess.waitForStarted();
+    if (installerProcess.state() == QProcess::NotRunning) {
+        emit error (tr("Failed to start installer process."));
+        return;
+    }
+
+    installerProcess.waitForFinished();
+
+    if (installerProcess.exitStatus() == QProcess::CrashExit) {
+        /* Woops */
+        qWarning() << "Installer process crashed";
+    } else if (installerProcess.exitStatus() != QProcess::NormalExit) {
+        /* Can not Happen. there are only those two values but maybe
+         * qt changed.. */
+        qWarning() << "Exit status neither normal nor crash.";
+        return;
+    }
+
+    if (installerProcess.exitCode() == 0) {
+        qDebug() << "output: " << installerProcess.readAllStandardOutput();
+    } else {
+        /* TODO handle errors defined by errorcodes.h */
+        qDebug() << "Installer Process returned: " << installerProcess.exitCode();
+        qDebug() << "output: " << installerProcess.readAllStandardOutput();
+        return;
+    }
+#endif
 }
-#else
-void InstallWrapper::run()
-{
-}
-#endif
--- a/ui/installwrapper.h	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/installwrapper.h	Tue Apr 15 09:44:54 2014 +0200
@@ -1,14 +1,24 @@
 #ifndef UI_INSTALLWRAPPER_H
 #define UI_INSTALLWRAPPER_H
 
+#include <QFileInfo>
 #include <QString>
 #include <QStringList>
+#include <QTemporaryFile>
 #include <QThread>
 
 #include "certificate.h"
 /** @file installwrapper.h
  * @brief Wrapper around the call to the updated process */
 
+/** @brief Get the FileInfo for the installation process.
+ *
+ * This points to the file where the installation process is
+ * supposed to be.
+ *
+ * @returns A FileInfo with the path of the installation process.*/
+QFileInfo getCinstProcInfo();
+
  /** @brief wrapper around installer process
   *
   * This wrapper is mostly needed because QProcess executes
@@ -36,14 +46,22 @@
      *
      * @param[in] parent the parent object.
      * @param[in] listFileName the absolute path to the certificatelist.
-     * @param[in] instructions a list of instructions to execute.
+     * @param[in] choices a list of R: / I: <cert> lines to execute.
      */
     InstallWrapper(QObject* parent, const QString& path,
                    const QStringList& instructions);
 
 private:
+    /**@brief write the choices made into a temporary file 
+     *
+     * @param[in] choicesFile the temporary file to write.
+     *
+     * @returns true on success. false on error.
+     */
+    bool writeChoices(QTemporaryFile* choicesFile) const;
+
     const QString mCertListFile;
-    const QStringList mInstructions;
+    const QStringList mChoices;
 
 protected:
     void run();
--- a/ui/listupdatedialog.cpp	Tue Apr 15 09:44:32 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-#include "listupdatedialog.h"
-#include <QCoreApplication>
-#include <QDebug>
-#include <QDir>
-#include <QFileInfo>
-#include <QProcess>
-#include <QPushButton>
-#include <QGroupBox>
-#include <QHBoxLayout>
-#include <QListWidget>
-#include <QVBoxLayout>
-#include <QLabel>
-#include <QMessageBox>
-
-
-#include "certificate.h"
-#include "installwrapper.h"
-
-ListUpdateDialog::ListUpdateDialog(QMainWindow *parent,
-                                   const CertificateList &listToInstall) :
-    QDialog(parent),
-    mCertificateList(listToInstall)
-{
-    setupGUI();
-}
-
-void ListUpdateDialog::setupGUI()
-{
-    /* Top level layout / widgets */
-    QVBoxLayout *topLayout = new QVBoxLayout;
-    QHBoxLayout *headerLayout = new QHBoxLayout;
-    QHBoxLayout *listLayout = new QHBoxLayout;
-    QHBoxLayout *bottomLayout = new QHBoxLayout;
-
-    QImage *logoImage = new QImage(":/img/logo-small.png");
-    QLabel *logo = new QLabel;
-    logo->setBackgroundRole(QPalette::Base);
-    logo->setPixmap(QPixmap::fromImage(*logoImage));
-    QLabel *title = new QLabel("<h3>" + tr("Update Certificate") + "</h3>");
-    headerLayout->addWidget(logo);
-    headerLayout->addWidget(title);
-    headerLayout->setStretch(0, 0);
-    headerLayout->setStretch(1, 10);
-
-    QPushButton *executeUpdate = new QPushButton(tr("Update Stores"));
-    connect(executeUpdate, &QPushButton::clicked,
-            this, &ListUpdateDialog::executeUpdate);
-    QPushButton *install = new QPushButton(tr("Install selected"));
-    QPushButton *later = new QPushButton(tr("Remind me later"));
-    bottomLayout->insertStretch(0, 10);
-    bottomLayout->addWidget(executeUpdate);
-    bottomLayout->addWidget(install);
-    bottomLayout->addWidget(later);
-
-    /* The certificate groups */
-    mCertListWidget = new QListWidget;
-    connect(mCertListWidget, SIGNAL(itemClicked(QListWidgetItem*)),
-        this, SLOT(showDetails(QListWidgetItem*)));
-    mDetailWidget = new QTextEdit;
-    mDetailWidget->setReadOnly(true);
-    listLayout->addWidget(mCertListWidget);
-    listLayout->addWidget(mDetailWidget);
-    QGroupBox *certGroup = new QGroupBox(tr("Select certificates"));
-    certGroup->setLayout(listLayout);
-
-    foreach (const Certificate& cert, mCertificateList.getCertificates()) {
-        if (!cert.isValid()) {
-            qWarning() << "Invalid certificate in list";
-            continue;
-        }
-        QListWidgetItem* item = new QListWidgetItem(cert.shortDescription());
-        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
-        item->setData(Qt::ToolTipRole, cert.details());
-        item->setData(Qt::UserRole, cert.base64Line());
-        item->setCheckState(Qt::Checked);
-        QIcon *certIcon = cert.isInstallCert() ? new QIcon(":/img/list-add.png") :
-                                                 new QIcon(":/img/list-remove.png");
-        item->setIcon(*certIcon);
-
-        mCertListWidget->addItem(item);
-    }
-
-    /* Fill top level layout */
-    topLayout->addLayout(headerLayout);
-    topLayout->addWidget(certGroup);
-    topLayout->addLayout(bottomLayout);
-
-    setLayout(topLayout);
-
-    return;
-}
-
-void ListUpdateDialog::installerError(const QString& errMsg) {
-    QMessageBox::warning(this, tr("Installation Error"), errMsg);
-}
-
-void ListUpdateDialog::executeUpdate() {
-
-    QStringList instructions;
-
-    for (int i = 0; i < mCertListWidget->count(); i++) {
-        QListWidgetItem *item = mCertListWidget->item(i);
-        if (item->checkState() == Qt::Checked) {
-            instructions << item->data(Qt::UserRole).toString();
-        }
-
-        /* TODO: Check if it was an install instruction for an old certificate
-         * (already installed) and remove it in case it is unchecked. */
-    }
-
-    InstallWrapper *instWrap = new InstallWrapper(this,
-                                                  mCertificateList.fileName(),
-                                                  instructions);
-    connect(instWrap, SIGNAL(finished()), instWrap, SLOT(deleteLater()));
-    connect(instWrap, SIGNAL(error(const QString &)),
-        this, SLOT(installerError(const QString &)));
-    instWrap->start();
-
-#if 0
-    /* TODO move this in another dialog and call it async*/
-    QProcess installerProcess;
-    QFileInfo fi(QCoreApplication::applicationFilePath());
-    QDir myDir = fi.absoluteDir();
-    QString instProcName = "cinst";
-    if (!fi.suffix().isEmpty()) {
-        instProcName += "." + fi.suffix();
-    }
-    QString instPath = myDir.absoluteFilePath(instProcName);
-
-    installerProcess.setProgram(instPath);
-    installerProcess.start();
-    installerProcess.waitForStarted();
-    if (installerProcess.state() == QProcess::NotRunning) {
-        qWarning() << "Failed to start installer Process.";
-        /* TODO ERROR message for the user */
-        return;
-    }
-
-    installerProcess.write("-----BEGIN CERTIFICATE LIST-----\r\n");
-    installerProcess.write(mCertificateList.rawData().toLatin1());
-    installerProcess.write("-----END CERTIFICATE LIST-----\r\n");
-
-    /* Items to install */
-    for (int i = 0; i < mCertListWidget->count(); i++) {
-        QListWidgetItem *item = mCertListWidget->item(i);
-        if (item->checkState() != Qt::Checked &&
-            item->data(Qt::UserRole).toString().startsWith("I:")) {
-            continue;
-        }
-        installerProcess.write(item->data(Qt::UserRole).toString().toLatin1());
-        installerProcess.write("\r\n");
-    }
-
-    /* Items to remove */
-    for (int i = 0; i < mCertListWidget->count(); i++) {
-        QListWidgetItem *item = mCertListWidget->item(i);
-        if (!item->checkState() != Qt::Checked &&
-            item->data(Qt::UserRole).toString().startsWith("R:")) {
-            continue;
-        }
-        installerProcess.write(item->data(Qt::UserRole).toString().toLatin1());
-        installerProcess.write("\r\n");
-    }
-
-    installerProcess.waitForBytesWritten();
-    installerProcess.closeWriteChannel();
-    installerProcess.waitForFinished();
-
-    if (installerProcess.exitStatus() == QProcess::CrashExit) {
-        /* Woops */
-        qWarning() << "Installer process crashed";
-    } else if (installerProcess.exitStatus() != QProcess::NormalExit) {
-        /* Can not Happen. there are only those two values but maybe
-         * qt changed.. */
-        qWarning() << "Exit status neither normal nor crash.";
-        return;
-    }
-
-    if (installerProcess.exitCode() == 0) {
-        qDebug() << "Success!";
-        qDebug() << "output: " << installerProcess.readAllStandardOutput();
-    } else {
-        /* TODO handle errors defined by errorcodes.h */
-        qDebug() << "Installer Process returned: " << installerProcess.exitCode();
-        qDebug() << "output: " << installerProcess.readAllStandardOutput();
-        return;
-    }
-#endif
-}
-
-void ListUpdateDialog::showDetails(QListWidgetItem *item)
-{
-    QString details = item->data(Qt::ToolTipRole).toString();
-    mDetailWidget->setPlainText(details);
-}
--- a/ui/listupdatedialog.h	Tue Apr 15 09:44:32 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#ifndef LISTUPDATEDIALOG_H
-#define LISTUPDATEDIALOG_H
-
-#include "certificatelist.h"
-#include <QDialog>
-#include <QMainWindow>
-#include <QTextEdit>
-#include <QListWidgetItem>
-/**
- * @file listupdatedialog.h
- * @brief The dialog for certificate selection.
- */
-
-class QListWidget;
-
-class ListUpdateDialog : public QDialog
-{
-    Q_OBJECT
-public:
-    /** @brief Create a list update dialog for the listToInstall */
-    ListUpdateDialog(QMainWindow *parent, const CertificateList &listToInstall);
-
-private:
-    CertificateList mCertificateList;
-    void setupGUI();
-
-    QListWidget *mCertListWidget;
-    QTextEdit *mDetailWidget;
-
-private slots:
-    void installerError(const QString& errMsg);
-    void executeUpdate();
-    void showDetails(QListWidgetItem*);
-};
-
-#endif // LISTUPDATEDIALOG_H
--- a/ui/main.cpp	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/main.cpp	Tue Apr 15 09:44:54 2014 +0200
@@ -46,10 +46,7 @@
     QApplication::setApplicationVersion(QString::fromLatin1(VERSION));
     QSettings::setDefaultFormat(QSettings::IniFormat);
 
-    MainWindow mainWin;
-    if (!trayMode) {
-        mainWin.show();
-    }
+    MainWindow mainWin(trayMode);
 
     return app.exec();
 }
--- a/ui/mainwindow.cpp	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/mainwindow.cpp	Tue Apr 15 09:44:54 2014 +0200
@@ -1,6 +1,7 @@
 #include "mainwindow.h"
 
 #include <QDebug>
+#include <QProgressDialog>
 #include <QMessageBox>
 #include <QSystemTrayIcon>
 #include <QAction>
@@ -19,7 +20,7 @@
 
 // The amount of time in minutes stay silent if we have
 // something to say
-#define NAG_INTERVAL_MINUTES 2
+#define NAG_INTERVAL_MINUTES 60
 
 #define SERVER_URL "https://files.kolab.org:443"
 #define LIST_RESOURCE "/incoming/aheinecke/test"
@@ -27,14 +28,16 @@
 
 #include "certificatelist.h"
 #include "downloader.h"
-#include "listupdatedialog.h"
 #include "helpdialog.h"
 #include "aboutdialog.h"
 #include "statusdialog.h"
 #include "certificateitemdelegate.h"
 #include "separatoritemdelegate.h"
+#include "installwrapper.h"
 
-MainWindow::MainWindow() {
+MainWindow::MainWindow(bool trayMode):
+    mTrayMode(trayMode)
+{
     createActions();
     createTrayIcon();
     createMenuBar();
@@ -50,6 +53,9 @@
     mMessageTimer->setInterval(NAG_INTERVAL_MINUTES * 60 * 1000);
     mMessageTimer->start();
     checkUpdates();
+    if (!trayMode) {
+        show();
+    }
 }
 
 void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
@@ -60,7 +66,7 @@
         showMessage();
         break;
     case QSystemTrayIcon::DoubleClick:
-        // TODO show menu
+        show();
         break;
     default:
         ;
@@ -70,16 +76,13 @@
 void MainWindow::messageClicked()
 {
     if (mCurState == NewListAvailable) {
-        ListUpdateDialog *listUpdateDialog = new ListUpdateDialog(this,
-                mListToInstall);
-        listUpdateDialog->show();
-        qDebug() << "NewListAvailable";
+        show();
     }
 }
 
 void MainWindow::showMessage()
 {
-    if (!mCurMessage.isEmpty()) {
+    if (!isVisible() && !mCurMessage.isEmpty()) {
         mTrayIcon->showMessage(QApplication::applicationName(), mCurMessage,
                                QSystemTrayIcon::Information, 5000);
         mMessageTimer->start(); // Restart the timer so that we don't spam
@@ -247,11 +250,11 @@
 
     // The certificate list
     QGroupBox *certBox = new QGroupBox(tr("Managed Certificates"));
-    certificateList = new QListWidget;
-    certificateList->setItemDelegate(new CertificateItemDelegate);
-    connect(certificateList, SIGNAL(itemClicked(QListWidgetItem*)),
+    mCertListWidget = new QListWidget;
+    mCertListWidget->setItemDelegate(new CertificateItemDelegate);
+    connect(mCertListWidget, SIGNAL(itemClicked(QListWidgetItem*)),
         this, SLOT(showDetails(QListWidgetItem*)));
-    certLayout->addWidget(certificateList);
+    certLayout->addWidget(mCertListWidget);
     certBox->setLayout(certLayout);
 
     // The header (icon, about text)
@@ -278,6 +281,7 @@
     installButton = new QPushButton(tr("Install selected"));
     installButton->setFixedHeight(80);
     connect(installButton, SIGNAL(clicked()), this, SLOT(resizeButtons()));
+    connect(installButton, SIGNAL(clicked()), this, SLOT(installCerts()));
     quitButton = new QPushButton(tr("Quit"));
     quitButton->setFixedHeight(20);
     connect(quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
@@ -313,7 +317,7 @@
 void MainWindow::loadCertificateList()
 {
     qDebug() << "display certificates";
-    certificateList->clear();
+    mCertListWidget->clear();
     int i = 0;
     foreach (const Certificate &cert, mListToInstall.getCertificates()) {
         if (!cert.isValid()) {
@@ -337,8 +341,8 @@
             }
             if (i == 3) {
                 QListWidgetItem *sep = new QListWidgetItem("New certificates");
-                certificateList->setItemDelegateForRow(i, separator);
-                certificateList->addItem(sep);
+                mCertListWidget->setItemDelegateForRow(i, separator);
+                mCertListWidget->addItem(sep);
                 i++;
             }
         }
@@ -354,7 +358,7 @@
                 item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
             }
         }
-        certificateList->addItem(item);
+        mCertListWidget->addItem(item);
         i++;
     }
 }
@@ -387,9 +391,7 @@
 
 void MainWindow::showDetails(QListWidgetItem *item)
 {
-    qDebug() << "show details";
     QString details = item->data(Qt::UserRole).toString();
-    details.append("\rInstalled: 1.1.1971\rRemoved: 1.1.1971");
     certificateDetails->setPlainText(details);
 }
 
@@ -398,3 +400,44 @@
     installButton->setFixedHeight(20);
     quitButton->setFixedHeight(80);
 }
+
+void MainWindow::installerError(const QString& errMsg) {
+    QMessageBox::warning(this, tr("Error executing update"), errMsg);
+}
+
+void MainWindow::installCerts() {
+    QStringList choices;
+
+    for (int i = 0; i < mCertListWidget->count(); i++) {
+        QListWidgetItem *item = mCertListWidget->item(i);
+        if (item->checkState() == Qt::Checked) {
+            choices << item->data(Qt::UserRole).toString();
+            continue;
+        }
+        QString certLine = item->data(Qt::UserRole).toString();
+        if (certLine.startsWith("I:")) {
+            certLine[0] = 'R';
+            choices << certLine;
+        }
+    }
+
+    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.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(error(const QString &)),
+        this, SLOT(installerError(const QString &)));
+    instWrap->start();
+
+}
--- a/ui/mainwindow.h	Tue Apr 15 09:44:32 2014 +0200
+++ b/ui/mainwindow.h	Tue Apr 15 09:44:54 2014 +0200
@@ -25,7 +25,16 @@
     Q_OBJECT
 
 public:
-    MainWindow();
+    /**@brief create a new Main Window object
+     *
+     * In tray mode this window is not shown and only shows
+     * notification messages if there is some actionable state
+     * reached. If tray mode is true it also exits after
+     * an update check.
+     *
+     * @param[in] trayMode set the tray mode
+     * */
+    MainWindow(bool trayMode);
 
     void setMessage(const QString message) {mCurMessage = message;}
     QString getMessage() {return mCurMessage;}
@@ -55,6 +64,8 @@
     void showAbout();
     void showDetails(QListWidgetItem*);
     void resizeButtons();
+    void installerError(const QString& errMsg);
+    void installCerts();
 
 private:
     /** @brief check the integrity of available files.
@@ -72,6 +83,9 @@
     void createContent();
     void loadCertificateList();
 
+    /* Are we running in tray mode ?*/
+    const bool mTrayMode;
+    /* The message currently shown at intervals */
     QString mCurMessage;
     QString mInstalledSWVersion;
     QString mInstalledListVersion;
@@ -88,7 +102,7 @@
 
     CertificateList mListToInstall;
 
-    QListWidget *certificateList;
+    QListWidget *mCertListWidget;
     QTextEdit *certificateDetails;
     QPushButton *installButton;
     QPushButton *quitButton;

http://wald.intevation.org/projects/trustbridge/