Mercurial > trustbridge
view ui/createinstallerdialog.cpp @ 754:27043d74dc90
(Issue25) Align header contents in their own column.
We now also stretch column 3 so that the contents are aligned
with the descriptive labels without a space in between.
Sadly this causes the quit button to be resized to it's minimum
instead of sharing the space with the installation button as the
installation button is so large that it squeezes the push button.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 07 Jul 2014 12:38:33 +0200 |
parents | 6c4fff146999 |
children | 49168bcb02e2 |
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 "createinstallerdialog.h" #include <QDebug> #include <QTextEdit> #include <QDir> #include <QPushButton> #include <QGroupBox> #include <QHBoxLayout> #include <QVBoxLayout> #include <QLabel> #include <QFileDialog> #include <QSettings> #include <QStyle> #include <QApplication> #include <QMessageBox> #include <QTemporaryDir> /* Static information used in codesigning */ #ifndef SIGN_HASH #define SIGN_HASH "sha256" #endif #ifndef SIGN_URL #define SIGN_URL "https://wald.intevation.org/projects/trustbridge/" #endif #ifndef SIGN_PUBLISHER #define SIGN_PUBLISHER "TrustBridge Test with ümlaut" #endif CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) : QDialog(parent), mProgress(this), mInstallerPath(), mCurrentWorkingDir(NULL) { QSettings settings; setWindowTitle(tr("Create binary installer")); setupGUI(); resize(500, 250); mCertFile->setText(settings.value("CodeSignCert", QString()).toString()); mBinaryFolder->setText(settings.value("LastBinaryFolder", QString()).toString()); mSaveFile->setText(settings.value("LastBinOutputFolder", QString()).toString()); connect(&mNSISProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); connect(&mNSISProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); } void CreateInstallerDialog::setupGUI() { /* Top level layout / widgets */ QVBoxLayout *topLayout = new QVBoxLayout; QVBoxLayout *headerLayout = new QVBoxLayout; QHBoxLayout *headerSubLayout = new QHBoxLayout; QHBoxLayout *centerLayout = new QHBoxLayout; QHBoxLayout *bottomLayout = new QHBoxLayout; QVBoxLayout *labelLayout = new QVBoxLayout; QVBoxLayout *fieldLayout = new QVBoxLayout; QVBoxLayout *buttonLayout = new QVBoxLayout; QLabel *header = new QLabel("<h3>" + tr("Create binary installer") + "</h3>"); QLabel *description = new QLabel( tr("Create and sign a TrustBridge binary installer.")); headerSubLayout->insertSpacing(0, 40); headerSubLayout->addWidget(description); QFrame *headerSeparator = new QFrame(); headerSeparator->setFrameShape(QFrame::HLine); headerSeparator->setFrameShadow(QFrame::Sunken); headerLayout->addWidget(header); headerLayout->addLayout(headerSubLayout); headerLayout->addWidget(headerSeparator); headerLayout->insertSpacing(4, 10); QLabel *archiveLabel = new QLabel(tr("Select binary folder:")); QLabel *certLabel = new QLabel(tr("Select code signing certificate:")); QLabel *saveLabel = new QLabel(tr("Select output folder:")); labelLayout->addWidget(archiveLabel); labelLayout->addWidget(certLabel); labelLayout->addWidget(saveLabel); mBinaryFolder = new QLineEdit(); mCertFile = new QLineEdit(); mSaveFile = new QLineEdit(); fieldLayout->addWidget(mBinaryFolder); fieldLayout->addWidget(mCertFile); fieldLayout->addWidget(mSaveFile); QPushButton *archiveSelect = new QPushButton("..."); connect(archiveSelect, SIGNAL(clicked()), this, SLOT(openFolderSelect())); archiveSelect->setFixedWidth(30); QPushButton *certSelect = new QPushButton("..."); connect(certSelect, SIGNAL(clicked()), this, SLOT(openCertificateSelect())); certSelect->setFixedWidth(30); QPushButton *saveSelect = new QPushButton("..."); connect(saveSelect, SIGNAL(clicked()), this, SLOT(openSaveLocation())); saveSelect->setFixedWidth(30); buttonLayout->addWidget(archiveSelect); buttonLayout->addWidget(certSelect); buttonLayout->addWidget(saveSelect); centerLayout->addLayout(labelLayout); centerLayout->addLayout(fieldLayout); centerLayout->addLayout(buttonLayout); QPushButton *create = new QPushButton(tr("Create installer")); connect(create, SIGNAL(clicked()), this, SLOT(createInstaller())); QPushButton *cancel = new QPushButton(tr("Cancel")); connect(cancel, SIGNAL(clicked()), this, SLOT(close())); bottomLayout->insertStretch(0, 10); bottomLayout->addWidget(create); bottomLayout->addWidget(cancel); QFrame *bottomSeparator = new QFrame(); bottomSeparator->setFrameShape(QFrame::HLine); bottomSeparator->setFrameShadow(QFrame::Sunken); topLayout->addLayout(headerLayout); topLayout->addLayout(centerLayout); topLayout->insertStretch(2, 10); centerLayout->insertSpacing(3, 10); topLayout->addWidget(bottomSeparator); topLayout->addLayout(bottomLayout); setLayout(topLayout); mProgress.setWindowModality(Qt::WindowModal); mProgress.setCancelButton(0); mProgress.setRange(0,0); mProgress.setMinimumDuration(0); return; } void CreateInstallerDialog::openCertificateSelect() { QSettings settings; QString certFile = QFileDialog::getOpenFileName( this, tr("Select certificate"), mCertFile->text().isEmpty() ? QDir::homePath() : mCertFile->text(), "*.pem *.der *.crt"); settings.setValue("CodeSignCert", certFile); mCertFile->setText(certFile); } void CreateInstallerDialog::openFolderSelect() { QSettings settings; QString archiveFolder = QFileDialog::getExistingDirectory( this, tr("Select binary folder"), mBinaryFolder->text().isEmpty() ? QDir::homePath() : mBinaryFolder->text()); mBinaryFolder->setText(archiveFolder); settings.setValue("LastBinaryFolder", archiveFolder); } void CreateInstallerDialog::openSaveLocation() { QSettings settings; QString saveFile = QFileDialog::getExistingDirectory( this, tr("Select target location"), mSaveFile->text().isEmpty() ? QDir::homePath() : mSaveFile->text()); mSaveFile->setText(saveFile); settings.setValue("LastBinOutputFolder", saveFile); } void CreateInstallerDialog::showErrorMessage(const QString &msg) { QMessageBox::warning(this, tr("Error!"), msg); } void CreateInstallerDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (mCurrentWorkingDir) { delete mCurrentWorkingDir; mCurrentWorkingDir = NULL; } FinishedDialog *fin = new FinishedDialog(0, tr("Created installer in %1.") .arg(mSaveFile->text()), mNSISProc.readAll(), false); qDebug() << "Finished: " << mNSISProc.readAll(); mProgress.setLabelText(tr("Signing installer package...")); if (!signFile(mInstallerPath)) { showErrorMessage(tr("Failed to sign installer package.")); QFile::remove(mInstallerPath); } mProgress.cancel(); fin->show(); close(); } void CreateInstallerDialog::processError(QProcess::ProcessError error) { if (mCurrentWorkingDir) { delete mCurrentWorkingDir; mCurrentWorkingDir = NULL; } qDebug() << "Error: " << mNSISProc.readAll(); mProgress.cancel(); } void CreateInstallerDialog::createInstaller() { mProgress.setLabelText(tr("Creating installer package...")); QDir binDir(mBinaryFolder->text()); QDir outDir(mSaveFile->text()); if (mBinaryFolder->text().isEmpty() || !binDir.exists()) { showErrorMessage(tr("Please select an existing input folder.")); return; } if (mCertFile->text().isEmpty()) { showErrorMessage(tr("Please select a codesigning certificate.")); return; } if (mSaveFile->text().isEmpty() || !outDir.exists()) { showErrorMessage(tr("Please select a output folder.")); return; } QSettings options(binDir.filePath("meta.ini"), QSettings::IniFormat); options.sync(); QStringList keys = options.allKeys(); if (options.status() != QSettings::NoError || keys.size() < 1) { showErrorMessage(tr("Folder %1 does not appear to contain a meta.ini") .arg(binDir.path())); return; } QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows"); if (!signedFilesDir) { /* Error messages should have been shown by the codesign function */ return; } mProgress.setLabelText(tr("Creating NSIS package...")); /* Copy windows directory contents to tmpdir */ QStringList arguments; mNSISProc.setProgram("makensis"); mNSISProc.setProcessChannelMode(QProcess::MergedChannels); mNSISProc.setWorkingDirectory(outDir.path()); #ifdef Q_OS_WIN arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\"); arguments << "/Dpath_sep=\\"; foreach (const QString &key, keys) { QString value = options.value(key, QString()).toString(); if (key == "setupname") { value = value.arg(outDir.path().replace("/", "\\") + "\\"); mInstallerPath = value; } arguments << QString::fromLatin1("/D%1=%2").arg(key, value); } #else arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path(); arguments << "-Dpath_sep=/"; foreach (const QString &key, keys) { QString value = options.value(key, QString()).toString(); if (key == "setupname") { value = value.arg(outDir.path() + "/"); mInstallerPath = value; } arguments << QString::fromLatin1("-D%1=%2").arg(key, value); } #endif arguments << binDir.path() + "/trustbridge.nsi"; qDebug() << "Starting makensis with arguments: " << arguments; mNSISProc.setArguments(arguments); mNSISProc.start(); mProgress.show(); if (!mNSISProc.waitForStarted() || mNSISProc.state() == QProcess::NotRunning) { showErrorMessage(tr("Failed to start makensis.\n" "Please ensure that makensis is installed and in your PATH variable.")); } } bool CreateInstallerDialog::signFile(QString filePath) { QProcess signProc; signProc.setProcessChannelMode(QProcess::MergedChannels); signProc.setProgram("osslsigncode"); QStringList arguments; QSettings mySettings; QString publisher = mySettings.value("sign_publisher", SIGN_PUBLISHER).toString(); QString url = mySettings.value("sign_url", SIGN_URL).toString(); QString hash = mySettings.value("sign_hash", SIGN_HASH).toString(); arguments << "sign" << "-certs" << mCertFile->text() << "-key" << mCertFile->text() << "-n" << publisher << "-i" << url << "-h" << hash << "-in" << filePath << "-out" << filePath + ".signed"; qDebug() << "Starting osslsigncode with arguments: " << arguments; signProc.setArguments(arguments); signProc.start(); if (!signProc.waitForFinished(30000)) { qDebug() << "Signing takes longer then 30 seconds. Aborting."; return false; } if (signProc.exitStatus() != QProcess::NormalExit || signProc.exitCode() != 0) { qDebug() << "Error process returned: " << signProc.exitCode(); qDebug() << "Output: " << signProc.readAllStandardOutput(); return false; } if (!QFile::remove(filePath)) { qDebug() << "Failed to remove file."; return false; } if (!QFile::copy(filePath + ".signed", filePath)) { qDebug() << "Failed to copy signed file in place."; return false; } if (!QFile::remove(filePath + ".signed")) { qDebug() << "Failed to remove signed."; return false; } return true; } QTemporaryDir *CreateInstallerDialog::codesignBinaries(const QDir& sourceDir) { QTemporaryDir* target = new QTemporaryDir(); /* Copy all files from the source dir to a temporary location */ mProgress.setLabelText(tr("Signing binaries...")); mProgress.show(); foreach (const QFileInfo& entry, sourceDir.entryInfoList()) { QString targetPath = target->path() + QString::fromLatin1("/") + entry.fileName(); if (entry.fileName() == "." || entry.fileName() == "..") { continue; } if (!QFile::copy(entry.absoluteFilePath(), targetPath)) { qDebug() << "Failed to copy: " << entry.absoluteFilePath() << " to: " << targetPath; showErrorMessage(tr("Failed to copy binaries to temporary location.")); mProgress.cancel(); return NULL; } if (entry.suffix() == "exe") { if (!signFile(targetPath)) { showErrorMessage(tr("Failed to sign binaries with osslsigncode.\n" "Please check that %1 is a valid code signing certificate and that" "osslsigncode can be found in the PATH.").arg(mCertFile->text())); mProgress.cancel(); return NULL; } } } mProgress.cancel(); return target; } FinishedDialog::FinishedDialog(QDialog *parent, QString msg, QString details, bool isErr): QDialog(parent) { QVBoxLayout *topLayout = new QVBoxLayout; QHBoxLayout *buttonLayout = new QHBoxLayout; QLabel *msgLabel = new QLabel; QTextEdit *detailsWindow = new QTextEdit; detailsWindow->insertPlainText(details); detailsWindow->setReadOnly(true); detailsWindow->hide(); if (!isErr) { setWindowTitle(tr("Success!")); msgLabel->setPixmap(QApplication::style()->standardIcon( QStyle::SP_MessageBoxInformation).pixmap(16, 16)); } else { setWindowTitle(tr("Error!")); msgLabel->setPixmap(QApplication::style()->standardIcon( QStyle::SP_MessageBoxCritical).pixmap(16, 16)); } msgLabel->setText(msg); topLayout->addWidget(msgLabel); topLayout->addWidget(detailsWindow); QPushButton *detailsBtn = new QPushButton(tr("Details")); connect(detailsBtn, SIGNAL(clicked()), detailsWindow, SLOT(show())); buttonLayout->addWidget(detailsBtn); QPushButton *okBtn = new QPushButton(tr("OK")); okBtn->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOkButton)); connect(okBtn, SIGNAL(clicked()), this, SLOT(close())); buttonLayout->insertStretch(0, 100); buttonLayout->addWidget(okBtn); topLayout->addLayout(buttonLayout); setLayout(topLayout); }