Mercurial > trustbridge
view ui/installwrapper.cpp @ 1075:7e1fd6d96714
(issue109) Feedback improved. Shell translation updated.
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Thu, 11 Sep 2014 10:18:46 +0200 |
parents | 317ee9dc4684 |
children | a12e6172d82c |
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 "installwrapper.h" #include <QFileInfo> #include <QProcess> #include <QTemporaryFile> #include <QApplication> #include <QDir> #include <QDebug> #include "logging.h" #include "util.h" #define INSTALL_TIMEOUT 3600000 /* Wait up to an hour */ InstallWrapper::InstallWrapper(QObject* parent, const QString& path, const QStringList& choices): QThread(parent), mCertListFile(path), mChoices(choices) { } QFileInfo getCinstProcInfo() { QFileInfo fi(QCoreApplication::applicationFilePath()); QDir myDir = fi.absoluteDir(); QString instProcName = "cinst"; if (!fi.suffix().isEmpty()) { instProcName += "." + fi.suffix(); } return QFileInfo(myDir.absoluteFilePath(instProcName)); } bool InstallWrapper::writeChoices(QTemporaryFile* choicesFile) const { if (!choicesFile->open()) { return false; } foreach (const QString &b64data, mChoices) { if (choicesFile->write(b64data.toLatin1()) == -1) { return false; } if (choicesFile->write("\n") == -1) { 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 choicesFile; QFileInfo cinstProcInfo = getCinstProcInfo(); QString cinstFileName = QDir::toNativeSeparators( getCinstProcInfo().absoluteFilePath()); if (!cinstProcInfo.isExecutable()) { emit error(tr("Could not find certificate installation process.")); return; } if (!writeChoices(&choicesFile)) { emit error(tr("Failed to write temporary file.")); return; } #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)); /* Windows needs each parameter packed in " */ QString parameters = "\"list=" + mCertListFile + "\" \"choices=" + choicesFile.fileName() + "\""; if (g_debug) { parameters += " --debug"; } shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; if (!is_system_install()) { shExecInfo.lpVerb = L"open"; } else { shExecInfo.lpVerb = L"runas"; } shExecInfo.lpFile = reinterpret_cast<LPCWSTR> (cinstFileName.utf16()); shExecInfo.lpParameters = reinterpret_cast<LPCWSTR> (parameters.utf16()); qDebug() << "Starting process " << cinstFileName <<" params: " << parameters; if (!ShellExecuteExW(&shExecInfo)) { char* errmsg = getLastErrorMsg(); QString qerrmsg = QString::fromUtf8(errmsg); free(errmsg); emit error(tr("Error executing process: %1").arg(qerrmsg)); return; } retval = WaitForSingleObject(shExecInfo.hProcess, INSTALL_TIMEOUT); if (retval != WAIT_OBJECT_0) { if (retval == WAIT_FAILED) { char* errmsg = getLastErrorMsg(); QString qerrmsg = QString::fromUtf8(errmsg); free(errmsg); emit error (tr("Error monitoring process: %1").arg(qerrmsg)); return; } else { emit error (tr("Certificate installation timed out.")); return; } } if (GetExitCodeProcess(shExecInfo.hProcess, &retval)) { if (retval == STILL_ACTIVE) { qDebug() << "Process still running, huh.."; } } else { char* errmsg = getLastErrorMsg(); QString qerrmsg = QString::fromUtf8(errmsg); free(errmsg); emit error (tr("Failed to check process status: %1").arg(qerrmsg)); CloseHandle(shExecInfo.hProcess); return; } CloseHandle(shExecInfo.hProcess); if (retval != 0) { /* TODO make this nicer */ emit error (tr("The process failed with return code. %1").arg(retval)); return; } #else /* WIN32 */ QProcess installerProcess; QStringList parameters; choicesFile.setAutoRemove(false); parameters << "list=" + mCertListFile << "choices=" + choicesFile.fileName(); if (g_debug) { parameters << "--debug"; } bool sudo_started = false; bool use_sudo = is_admin() && is_system_install(); if (use_sudo) { QStringList sudoPrograms; sudoPrograms << "pkexec" << "kdesudo" << "sudo"; QStringList sudoParams; sudoParams << cinstProcInfo.absoluteFilePath() << parameters; installerProcess.setArguments(sudoParams); foreach (const QString &sProg, sudoPrograms) { installerProcess.setProgram(sProg); qDebug() << "Starting process " << sProg <<" params: " << sudoParams; installerProcess.start(); if (!installerProcess.waitForStarted() || installerProcess.state() == QProcess::NotRunning) { continue; } else { sudo_started = true; break; } } } /* Fallback to start without sudo */ if (!use_sudo || !sudo_started) { installerProcess.setProgram(cinstProcInfo.absoluteFilePath()); installerProcess.setArguments(parameters); qDebug() << "Starting process " << cinstFileName <<" params: " << parameters; installerProcess.start(); if (!installerProcess.waitForStarted() || installerProcess.state() == QProcess::NotRunning) { emit error (tr("Failed to start installer process.")); return; } } installerProcess.waitForFinished(); if (installerProcess.exitStatus() == QProcess::CrashExit) { /* Woops */ emit error (tr("Failed to complete installation.")); return; } else if (installerProcess.exitStatus() != QProcess::NormalExit) { /* Can not Happen. there are only those two values but maybe * qt changed.. */ emit error (tr("Failed to complete installation.")); 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 emit installationSuccessful(); }