diff --git a/CMakeLists.txt b/CMakeLists.txt index 2716b3c40..204cf6c67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,151 +1,151 @@ set(kleopatra_version 3.1.8) # The following is for Windows. Keep in line with kleopatra_version. set(kleopatra_fileversion 3,1,8,0) cmake_minimum_required(VERSION 3.5) project(kleopatra VERSION ${kleopatra_version}) # Add version suffix for internal usage of the version string set(kleopatra_version ${kleopatra_version}${KLEOPATRA_VERSION_SUFFIX}) set(KDE_APPLICATIONS_VERSION "19.11.70") option(FORCE_DISABLE_KCMUTILS "Force building Kleopatra without KCMUtils. Doing this will disable configuration KCM Plugins. [default=OFF]" OFF) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF5_MIN_VERSION "5.63.0") set(KMIME_VERSION "5.12.40") set(LIBKLEO_VERSION "5.12.40") set(QT_REQUIRED_VERSION "5.11.0") set(GPGME_REQUIRED_VERSION "1.8.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(GenerateExportHeader) include(ECMGenerateHeaders) include(FeatureSummary) include(CheckFunctionExists) include(ECMGeneratePriFile) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) # Find KF5 packages if (NOT FORCE_DISABLE_KCMUTILS) find_package(KF5KCMUtils ${KF5_MIN_VERSION} CONFIG REQUIRED) endif() find_package(KF5WidgetsAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG) find_package(KF5Crash ${KF5_MIN_VERSION} REQUIRED) set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Documentation tools" TYPE OPTIONAL PURPOSE "Required to generate Kleopatra documentation.") # Optional packages if (WIN32) # Only a replacement available for Windows so this # is required on other platforms. find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus" PURPOSE "DBus session integration" URL "https://inqlude.org/libraries/kdbusaddons.html" TYPE OPTIONAL) else() find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) set(_kleopatra_dbusaddons_libs KF5::DBusAddons) endif() set(HAVE_QDBUS ${Qt5DBus_FOUND}) find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) # Kdepimlibs packages find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_VERSION} CONFIG REQUIRED) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(Assuan2 REQUIRED) set(HAVE_KCMUTILS ${KF5KCMUtils_FOUND}) find_package(Boost 1.34.0 REQUIRED) find_path(Boost_TOPOLOGICAL_SORT_DIR NAMES boost/graph/topological_sort.hpp PATHS ${Boost_INCLUDE_DIRS}) if(NOT Boost_TOPOLOGICAL_SORT_DIR) message(FATAL_ERROR "The Boost Topological_sort header was NOT found. Should be part of Boost graph module.") endif() set(kleopatra_release FALSE) if(NOT kleopatra_release) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra OUTPUT_VARIABLE Kleopatra_WC_REVISION) string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}") execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%ci ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE) string(REGEX REPLACE " [-0-9:+ ]*\n" "" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}") set(kleopatra_version "${kleopatra_version}-git${Kleopatra_WC_REVISION} (${Kleopatra_WC_LAST_CHANGED_DATE})") endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h) include (ConfigureChecks.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIR} ${ASSUAN2_INCLUDES} ) add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS) #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) - +add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() kde_enable_exceptions() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() install(FILES kleopatra.renamecategories kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) if(KF5DocTools_FOUND) add_subdirectory(doc) endif() diff --git a/src/commands/command.cpp b/src/commands/command.cpp index 25be8551f..24d70614e 100644 --- a/src/commands/command.cpp +++ b/src/commands/command.cpp @@ -1,314 +1,315 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/command.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "command.h" #include "command_p.h" #include "signencryptfilescommand.h" #include "importcertificatefromfilecommand.h" #include "decryptverifyfilescommand.h" #include "detailscommand.h" #include "lookupcertificatescommand.h" #include "checksumverifyfilescommand.h" #include #include #include #include "kleopatra_debug.h" #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; Command::Private::Private(Command *qq, KeyListController *controller) : q(qq), autoDelete(true), warnWhenRunningAtShutdown(true), indexes_(), view_(), parentWId(0), controller_(controller) { } Command::Private::~Private() { qCDebug(KLEOPATRA_LOG); } Command::Command(KeyListController *p) : QObject(p), d(new Private(this, p)) { if (p) { p->registerCommand(this); } } Command::Command(QAbstractItemView *v, KeyListController *p) : QObject(p), d(new Private(this, p)) { if (p) { p->registerCommand(this); } if (v) { setView(v); } } Command::Command(Private *pp) : QObject(pp->controller_), d(pp) { if (pp->controller_) { pp->controller_->registerCommand(this); } } Command::Command(QAbstractItemView *v, Private *pp) : QObject(pp->controller_), d(pp) { if (pp->controller_) { pp->controller_->registerCommand(this); } if (v) { setView(v); } } Command::Command(const GpgME::Key &key) : QObject(nullptr), d(new Private(this, nullptr)) { d->keys_ = std::vector(1, key); } Command::Command(const std::vector &keys) : QObject(nullptr), d(new Private(this, nullptr)) { d->keys_ = keys; } Command::Command(const Key &key, Private *pp) : QObject(nullptr), d(pp) { d->keys_ = std::vector(1, key); } Command::Command(const std::vector &keys, Private *pp) : QObject(nullptr), d(pp) { d->keys_ = keys; } Command::~Command() { qCDebug(KLEOPATRA_LOG); } void Command::setAutoDelete(bool on) { d->autoDelete = on; } bool Command::autoDelete() const { return d->autoDelete; } void Command::setWarnWhenRunningAtShutdown(bool on) { d->warnWhenRunningAtShutdown = on; } bool Command::warnWhenRunningAtShutdown() const { return d->warnWhenRunningAtShutdown; } void Command::setParentWidget(QWidget *widget) { d->parentWidget_ = widget; } void Command::setParentWId(WId wid) { d->parentWId = wid; } void Command::setView(QAbstractItemView *view) { if (view == d->view_) { return; } d->view_ = view; if (!view || !d->indexes_.empty()) { return; } const QItemSelectionModel *const sm = view->selectionModel(); if (!sm) { qCWarning(KLEOPATRA_LOG) << "view " << (void *)view << " has no selectionModel!"; return; } const QList selected = sm->selectedRows(); if (!selected.empty()) { std::copy(selected.begin(), selected.end(), std::back_inserter(d->indexes_)); return; } } void Command::setIndex(const QModelIndex &idx) { d->indexes_.clear(); d->indexes_.push_back(idx); } void Command::setIndexes(const QList &idx) { d->indexes_.clear(); std::copy(idx.begin(), idx.end(), std::back_inserter(d->indexes_)); } void Command::setKey(const Key &key) { d->keys_.clear(); if (!key.isNull()) { d->keys_.push_back(key); } } void Command::setKeys(const std::vector &keys) { d->keys_ = keys; } void Command::start() { doStart(); } void Command::cancel() { qCDebug(KLEOPATRA_LOG) << metaObject()->className(); doCancel(); Q_EMIT canceled(); } void Command::addTemporaryView(const QString &title, AbstractKeyListSortFilterProxyModel *proxy, const QString &tabToolTip) { if (TabWidget *const tw = d->controller_ ? d->controller_->tabWidget() : nullptr) if (QAbstractItemView *const v = tw->addTemporaryView(title, proxy, tabToolTip)) { setView(v); } } void Command::applyWindowID(QWidget *w) const { if (w) { if (d->parentWId) { if (QWidget *pw = QWidget::find(d->parentWId)) { w->setParent(pw, w->windowFlags()); } else { - KWindowSystem::setMainWindow(w, d->parentWId); + w->setAttribute(Qt::WA_NativeWindow, true); + KWindowSystem::setMainWindow(w->windowHandle(), d->parentWId); } } else { w->setParent(d->parentWidgetOrView(), w->windowFlags()); } } } // static QVector Command::commandsForFiles(const QStringList &files) { QStringList importFiles, decryptFiles, encryptFiles, checksumFiles; QVector cmds; for (const QString &fileName : files) { const unsigned int classification = classify(fileName); if (classification & Class::AnyCertStoreType) { importFiles << fileName; } else if (classification & Class::AnyMessageType) { // For any message we decrypt / verify. This includes // the class CipherText decryptFiles << fileName; } else if (isChecksumFile(fileName)) { checksumFiles << fileName; } else { QFileInfo fi(fileName); if (fi.isReadable()) { encryptFiles << fileName; } } } if (!importFiles.isEmpty()) { cmds << new ImportCertificateFromFileCommand(importFiles, nullptr); } if (!decryptFiles.isEmpty()) { cmds << new DecryptVerifyFilesCommand(decryptFiles, nullptr); } if (!encryptFiles.isEmpty()) { cmds << new SignEncryptFilesCommand(encryptFiles, nullptr); } if (!checksumFiles.isEmpty()) { cmds << new ChecksumVerifyFilesCommand(checksumFiles, nullptr); } return cmds; } // static Command *Command::commandForQuery(const QString &query) { const auto cache = Kleo::KeyCache::instance(); GpgME::Key key = cache->findByKeyIDOrFingerprint(query.toLocal8Bit().data()); if (key.isNull() && query.size() > 16) { // Try to find by subkeyid std::vector id; id.push_back(query.right(16).toStdString()); auto keys = cache->findSubkeysByKeyID(id); if (keys.size()) { key = keys[0].parent(); } } if (key.isNull()) { return new LookupCertificatesCommand(query, nullptr); } else { return new DetailsCommand(key, nullptr); } } diff --git a/src/conf/appearanceconfigpage.cpp b/src/conf/appearanceconfigpage.cpp index b9a86639d..504f68ec1 100644 --- a/src/conf/appearanceconfigpage.cpp +++ b/src/conf/appearanceconfigpage.cpp @@ -1,83 +1,88 @@ /* -*- mode: c++; c-basic-offset:4 -*- conf/appearanceconfigpage.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2004,2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include - +#include #include "appearanceconfigpage.h" #include "appearanceconfigwidget.h" #include #include using namespace Kleo; using namespace Kleo::Config; AppearanceConfigurationPage::AppearanceConfigurationPage(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { QVBoxLayout *lay = new QVBoxLayout(this); mWidget = new AppearanceConfigWidget(this); lay->addWidget(mWidget); + +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(mWidget, &AppearanceConfigWidget::changed, this, QOverload<>::of(&Kleo::Config::AppearanceConfigurationPage::changed)); +#else + connect(mWidget, &AppearanceConfigWidget::changed, this, &Kleo::Config::AppearanceConfigurationPage::markAsChanged); +#endif load(); } void AppearanceConfigurationPage::load() { mWidget->load(); } void AppearanceConfigurationPage::save() { mWidget->save(); } void AppearanceConfigurationPage::defaults() { mWidget->defaults(); } extern "C" { Q_DECL_EXPORT KCModule *create_kleopatra_config_appear(QWidget *parent = nullptr, const QVariantList &args = QVariantList()) { AppearanceConfigurationPage *page = new AppearanceConfigurationPage(parent, args); page->setObjectName(QStringLiteral("kleopatra_config_appear")); return page; } } diff --git a/src/conf/cryptooperationsconfigpage.cpp b/src/conf/cryptooperationsconfigpage.cpp index a37eca50d..cdda69ee7 100644 --- a/src/conf/cryptooperationsconfigpage.cpp +++ b/src/conf/cryptooperationsconfigpage.cpp @@ -1,82 +1,86 @@ /* -*- mode: c++; c-basic-offset:4 -*- conf/cryptooperationsconfigpage.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2010 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include +#include #include "cryptooperationsconfigpage.h" #include "cryptooperationsconfigwidget.h" #include using namespace Kleo; using namespace Kleo::Config; CryptoOperationsConfigurationPage::CryptoOperationsConfigurationPage(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); mWidget = new CryptoOperationsConfigWidget(this); lay->addWidget(mWidget); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(mWidget, &CryptoOperationsConfigWidget::changed, this, QOverload<>::of(&Kleo::Config::CryptoOperationsConfigurationPage::changed)); - +#else + connect(mWidget, &CryptoOperationsConfigWidget::changed, this, &Kleo::Config::CryptoOperationsConfigurationPage::markAsChanged); +#endif load(); } void CryptoOperationsConfigurationPage::load() { mWidget->load(); } void CryptoOperationsConfigurationPage::save() { mWidget->save(); } void CryptoOperationsConfigurationPage::defaults() { mWidget->defaults(); } extern "C" { Q_DECL_EXPORT KCModule *create_kleopatra_config_cryptooperations(QWidget *parent = nullptr, const QVariantList &args = QVariantList()) { CryptoOperationsConfigurationPage *page = new CryptoOperationsConfigurationPage(parent, args); page->setObjectName(QStringLiteral("kleopatra_config_cryptooperations")); return page; } } diff --git a/src/conf/gnupgsystemconfigurationpage.cpp b/src/conf/gnupgsystemconfigurationpage.cpp index 204c79d6e..897b6507b 100644 --- a/src/conf/gnupgsystemconfigurationpage.cpp +++ b/src/conf/gnupgsystemconfigurationpage.cpp @@ -1,99 +1,104 @@ /* -*- mode: c++; c-basic-offset:4 -*- conf/gnupgsystemconfigurationpage.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include - +#include #include "gnupgsystemconfigurationpage.h" #include #include #include #include using namespace Kleo::Config; GnuPGSystemConfigurationPage::GnuPGSystemConfigurationPage(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig(); mWidget = new CryptoConfigModule(config, CryptoConfigModule::TabbedLayout, this); lay->addWidget(mWidget); + +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(mWidget, &CryptoConfigModule::changed, this, QOverload<>::of(&Kleo::Config::GnuPGSystemConfigurationPage::changed)); +#else + connect(mWidget, &CryptoConfigModule::changed, this, &Kleo::Config::GnuPGSystemConfigurationPage::markAsChanged); +#endif load(); } GnuPGSystemConfigurationPage::~GnuPGSystemConfigurationPage() { // ### correct here? if (QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig()) { config->clear(); } } void GnuPGSystemConfigurationPage::load() { mWidget->reset(); } void GnuPGSystemConfigurationPage::save() { mWidget->save(); #if 0 // Tell other apps (e.g. kmail) that the gpgconf data might have changed QDBusMessage message = QDBusMessage::createSignal(QString(), "org.kde.kleo.CryptoConfig", "changed"); QDBusConnection::sessionBus().send(message); #endif } void GnuPGSystemConfigurationPage::defaults() { mWidget->defaults(); } extern "C" Q_DECL_EXPORT KCModule *create_kleopatra_config_gnupgsystem(QWidget *parent, const QVariantList &args) { GnuPGSystemConfigurationPage *page = new GnuPGSystemConfigurationPage(parent, args); page->setObjectName(QStringLiteral("kleopatra_config_gnupgsystem")); return page; } diff --git a/src/conf/smimevalidationconfigurationpage.cpp b/src/conf/smimevalidationconfigurationpage.cpp index edfb6a795..12b1a26dd 100644 --- a/src/conf/smimevalidationconfigurationpage.cpp +++ b/src/conf/smimevalidationconfigurationpage.cpp @@ -1,80 +1,83 @@ /* -*- mode: c++; c-basic-offset:4 -*- conf/smimevalidationconfigurationpage.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include - +#include #include "smimevalidationconfigurationpage.h" #include "smimevalidationconfigurationwidget.h" #include #include using namespace Kleo::Config; SMimeValidationConfigurationPage::SMimeValidationConfigurationPage(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { QVBoxLayout *lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); mWidget = new SMimeValidationConfigurationWidget(this); lay->addWidget(mWidget); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) connect(mWidget, &SMimeValidationConfigurationWidget::changed, this, QOverload<>::of(&Kleo::Config::SMimeValidationConfigurationPage::changed)); - +#else + connect(mWidget, &SMimeValidationConfigurationWidget::changed, this, &Kleo::Config::SMimeValidationConfigurationPage::markAsChanged); +#endif load(); } void SMimeValidationConfigurationPage::load() { mWidget->load(); } void SMimeValidationConfigurationPage::save() { mWidget->save(); } void SMimeValidationConfigurationPage::defaults() { mWidget->defaults(); } extern "C" Q_DECL_EXPORT KCModule *create_kleopatra_config_smimevalidation(QWidget *parent, const QVariantList &args) { SMimeValidationConfigurationPage *page = new SMimeValidationConfigurationPage(parent, args); page->setObjectName(QStringLiteral("kleopatra_config_smimevalidation")); return page; } diff --git a/src/kleopatraapplication.cpp b/src/kleopatraapplication.cpp index 45e70f462..e2e389896 100644 --- a/src/kleopatraapplication.cpp +++ b/src/kleopatraapplication.cpp @@ -1,658 +1,658 @@ /* kleopatraapplication.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "kleopatraapplication.h" #include "mainwindow.h" #include "kleopatra_options.h" #include "systrayicon.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_USABLE_ASSUAN # include #endif #include "commands/signencryptfilescommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/lookupcertificatescommand.h" #include "commands/checksumcreatefilescommand.h" #include "commands/checksumverifyfilescommand.h" #include "commands/detailscommand.h" #include "commands/newcertificatecommand.h" #include "dialogs/updatenotification.h" #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; static void add_resources() { KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra")); KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg")); } static QList default_logging_options() { QList result; result.push_back("io"); return result; } class KleopatraApplication::Private { friend class ::KleopatraApplication; KleopatraApplication *const q; public: explicit Private(KleopatraApplication *qq) : q(qq), ignoreNewInstance(true), firstNewInstance(true), sysTray(nullptr) { } ~Private() { delete sysTray; } void init() { KDAB_SET_OBJECT_NAME(readerStatus); #ifndef QT_NO_SYSTEMTRAYICON sysTray = new SysTrayIcon(); sysTray->setAnyCardHasNullPin(readerStatus.anyCardHasNullPin()); sysTray->setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys()); connect(&readerStatus, &SmartCard::ReaderStatus::anyCardHasNullPinChanged, sysTray, &SysTrayIcon::setAnyCardHasNullPin); connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged, sysTray, &SysTrayIcon::setAnyCardCanLearnKeys); #endif } private: void connectConfigureDialog() { if (configureDialog && q->mainWindow()) { connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } } void disconnectConfigureDialog() { if (configureDialog && q->mainWindow()) { disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } } public: bool ignoreNewInstance; bool firstNewInstance; QPointer configureDialog; QPointer mainWindow; SmartCard::ReaderStatus readerStatus; #ifndef QT_NO_SYSTEMTRAYICON SysTrayIcon *sysTray; #endif std::shared_ptr keyCache; std::shared_ptr log; std::shared_ptr watcher; public: void setupKeyCache() { keyCache = KeyCache::mutableInstance(); watcher.reset(new FileSystemWatcher); watcher->whitelistFiles(gnupgFileWhitelist()); watcher->addPath(gnupgHomeDirectory()); watcher->setDelay(1000); keyCache->addFileSystemWatcher(watcher); } void setupLogging() { log = Log::mutableInstance(); const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS"); const bool logAll = envOptions.trimmed() == "all"; const QList options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(','); const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR"); if (dirNative.isEmpty()) { return; } const QString dir = QFile::decodeName(dirNative); const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(QCoreApplication::applicationPid())); std::unique_ptr logFile(new QFile(logFileName)); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) { qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled"; return; } log->setOutputDirectory(dir); if (logAll || options.contains("io")) { log->setIOLoggingEnabled(true); } qInstallMessageHandler(Log::messageHandler); #ifdef HAVE_USABLE_ASSUAN if (logAll || options.contains("pipeio")) { KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug); } UiServer::setLogStream(log->logFile()); #endif } }; KleopatraApplication::KleopatraApplication(int &argc, char *argv[]) : QApplication(argc, argv), d(new Private(this)) { } void KleopatraApplication::init() { d->init(); add_resources(); d->setupKeyCache(); d->setupLogging(); #ifndef QT_NO_SYSTEMTRAYICON d->sysTray->show(); #endif setQuitOnLastWindowClosed(false); KWindowSystem::allowExternalProcessWindowActivation(); } KleopatraApplication::~KleopatraApplication() { // work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514 KSharedConfig::openConfig()->sync(); } namespace { typedef void (KleopatraApplication::*Func)(const QStringList &, GpgME::Protocol); } void KleopatraApplication::slotActivateRequested(const QStringList &arguments, const QString &workingDirectory) { QCommandLineParser parser; kleopatra_options(&parser); QString err; if (!arguments.isEmpty() && !parser.parse(arguments)) { err = parser.errorText(); } else if (arguments.isEmpty()) { // KDBusServices omits the application name if no other // arguments are provided. In that case the parser prints // a warning. parser.parse(QStringList() << QCoreApplication::applicationFilePath()); } if (err.isEmpty()) { err = newInstance(parser, workingDirectory); } if (!err.isEmpty()) { KMessageBox::sorry(nullptr, err.toHtmlEscaped(), i18n("Failed to execute command")); Q_EMIT setExitValue(1); return; } Q_EMIT setExitValue(0); } QString KleopatraApplication::newInstance(const QCommandLineParser &parser, const QString &workingDirectory) { if (d->ignoreNewInstance) { qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance"; return QString(); } QStringList files; const QDir cwd = QDir(workingDirectory); Q_FOREACH (const QString &file, parser.positionalArguments()) { // We do not check that file exists here. Better handle // these errors in the UI. if (QFileInfo(file).isAbsolute()) { files << file; } else { files << cwd.absoluteFilePath(file); } } GpgME::Protocol protocol = GpgME::UnknownProtocol; if (parser.isSet(QStringLiteral("openpgp"))) { qCDebug(KLEOPATRA_LOG) << "found OpenPGP"; protocol = GpgME::OpenPGP; } if (parser.isSet(QStringLiteral("cms"))) { qCDebug(KLEOPATRA_LOG) << "found CMS"; if (protocol == GpgME::OpenPGP) { return i18n("Ambiguous protocol: --openpgp and --cms"); } protocol = GpgME::CMS; } // Check for Parent Window id WId parentId = 0; if (parser.isSet(QStringLiteral("parent-windowid"))) { #ifdef Q_OS_WIN // WId is not a portable type as it is a pointer type on Windows. // casting it from an integer is ok though as the values are guaranteed to // be compatible in the documentation. parentId = reinterpret_cast(parser.value(QStringLiteral("parent-windowid")).toUInt()); #else parentId = parser.value(QStringLiteral("parent-windowid")).toUInt(); #endif } // Handle openpgp4fpr URI scheme QString needle; if (parser.isSet(QStringLiteral("search"))) { needle = parser.value(QStringLiteral("search")); } else if (parser.isSet(QStringLiteral("query"))) { needle = parser.value(QStringLiteral("query")); } if (needle.startsWith(QLatin1String("openpgp4fpr:"))) { needle.remove(0, 12); } // Check for --search command. if (parser.isSet(QStringLiteral("search"))) { // This is an extra command instead of a combination with the // similar query to avoid changing the older query commands behavior // and query's "show details if a certificate exist or search on a // keyserver" logic is hard to explain and use consistently. if (needle.isEmpty()) { return i18n("No search string specified for --search"); } LookupCertificatesCommand *const cmd = new LookupCertificatesCommand(needle, nullptr); cmd->setParentWId(parentId); cmd->start(); return QString(); } // Check for --query command if (parser.isSet(QStringLiteral("query"))) { if (needle.isEmpty()) { return i18n("No fingerprint argument specified for --query"); } auto cmd = Command::commandForQuery(needle); cmd->setParentWId(parentId); cmd->start(); return QString(); } // Check for --gen-key command if (parser.isSet(QStringLiteral("gen-key"))) { auto cmd = new NewCertificateCommand(nullptr); cmd->setParentWId(parentId); cmd->setProtocol(protocol); cmd->start(); return QString(); } // Check for --config command if (parser.isSet(QStringLiteral("config"))) { openConfigDialogWithForeignParent(parentId); return QString(); } static const QMap funcMap { { QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile }, { QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles }, { QStringLiteral("sign"), &KleopatraApplication::signFiles }, { QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles }, { QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles }, { QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles }, { QStringLiteral("verify"), &KleopatraApplication::verifyFiles }, { QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles }, { QStringLiteral("checksum"), &KleopatraApplication::checksumFiles }, }; QString found; Q_FOREACH (const QString &opt, funcMap.keys()) { if (parser.isSet(opt) && found.isEmpty()) { found = opt; } else if (parser.isSet(opt)) { return i18n("Ambiguous commands \"%1\" and \"%2\"", found, opt); } } QStringList errors; if (!found.isEmpty()) { if (files.empty()) { return i18n("No files specified for \"%1\" command", found); } qCDebug(KLEOPATRA_LOG) << "found" << found; (this->*funcMap.value(found))(files, protocol); } else { if (files.empty()) { if (!(d->firstNewInstance && isSessionRestored())) { qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow"; openOrRaiseMainWindow(); } } else { Q_FOREACH (const QString& fileName, files) { QFileInfo fi(fileName); if (!fi.isReadable()) { errors << i18n("Cannot read \"%1\"", fileName); } } Q_FOREACH (Command *cmd, Command::commandsForFiles(files)) { if (parentId) { cmd->setParentWId(parentId); } else { MainWindow *mw = mainWindow(); if (!mw) { mw = new MainWindow; mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } cmd->setParentWidget(mw); } cmd->start(); } } } d->firstNewInstance = false; #ifdef Q_OS_WIN // On Windows we might be started from the // explorer in any working directory. E.g. // a double click on a file. To avoid preventing // the folder from deletion we set the // working directory to the users homedir. QDir::setCurrent(QDir::homePath()); #endif return errors.join(QLatin1Char('\n')); } #ifndef QT_NO_SYSTEMTRAYICON const SysTrayIcon *KleopatraApplication::sysTrayIcon() const { return d->sysTray; } SysTrayIcon *KleopatraApplication::sysTrayIcon() { return d->sysTray; } #endif const MainWindow *KleopatraApplication::mainWindow() const { return d->mainWindow; } MainWindow *KleopatraApplication::mainWindow() { return d->mainWindow; } void KleopatraApplication::setMainWindow(MainWindow *mainWindow) { if (mainWindow == d->mainWindow) { return; } d->disconnectConfigureDialog(); d->mainWindow = mainWindow; #ifndef QT_NO_SYSTEMTRAYICON d->sysTray->setMainWindow(mainWindow); #endif d->connectConfigureDialog(); } static void open_or_raise(QWidget *w) { if (w->isMinimized()) { KWindowSystem::unminimizeWindow(w->winId()); w->raise(); } else if (w->isVisible()) { w->raise(); } else { w->show(); } } void KleopatraApplication::toggleMainWindowVisibility() { if (mainWindow()) { mainWindow()->setVisible(!mainWindow()->isVisible()); } else { openOrRaiseMainWindow(); } } void KleopatraApplication::restoreMainWindow() { qCDebug(KLEOPATRA_LOG) << "restoring main window"; // Sanity checks if (!isSessionRestored()) { qCDebug(KLEOPATRA_LOG) << "Not in session restore"; return; } if (mainWindow()) { qCDebug(KLEOPATRA_LOG) << "Already have main window"; return; } MainWindow *mw = new MainWindow; if (KMainWindow::canBeRestored(1)) { // restore to hidden state, Mainwindow::readProperties() will // restore saved visibility. mw->restore(1, false); } mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } void KleopatraApplication::openOrRaiseMainWindow() { MainWindow *mw = mainWindow(); if (!mw) { mw = new MainWindow; mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } open_or_raise(mw); UpdateNotification::checkUpdate(mw); } void KleopatraApplication::openConfigDialogWithForeignParent(WId parentWId) { if (!d->configureDialog) { d->configureDialog = new ConfigureDialog; d->configureDialog->setAttribute(Qt::WA_DeleteOnClose); d->connectConfigureDialog(); } // This is similar to what the commands do. if (parentWId) { if (QWidget *pw = QWidget::find(parentWId)) { d->configureDialog->setParent(pw, d->configureDialog->windowFlags()); } else { - KWindowSystem::setMainWindow(d->configureDialog, parentWId); + KWindowSystem::setMainWindow(d->configureDialog->windowHandle(), parentWId); } } open_or_raise(d->configureDialog); // If we have a parent we want to raise over it. if (parentWId) { d->configureDialog->raise(); } } void KleopatraApplication::openOrRaiseConfigDialog() { openConfigDialogWithForeignParent(0); } #ifndef QT_NO_SYSTEMTRAYICON void KleopatraApplication::startMonitoringSmartCard() { d->readerStatus.startMonitoring(); } #endif // QT_NO_SYSTEMTRAYICON void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/) { openOrRaiseMainWindow(); if (!files.empty()) { mainWindow()->importCertificatesFromFile(files); } } void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto) { SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, nullptr); cmd->setEncryptionPolicy(Force); cmd->setSigningPolicy(Allow); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto) { SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, nullptr); cmd->setSigningPolicy(Force); cmd->setEncryptionPolicy(Deny); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto) { SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, nullptr); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/) { DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Decrypt); cmd->start(); } void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Verify); cmd->start(); } void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->start(); } void KleopatraApplication::checksumFiles(const QStringList &files, GpgME::Protocol /*proto*/) { QStringList verifyFiles, createFiles; for (const QString &file : files) { if (isChecksumFile(file)) { verifyFiles << file; } else { createFiles << file; } } if (!verifyFiles.isEmpty()) { auto *const cmd = new ChecksumVerifyFilesCommand(verifyFiles, nullptr); cmd->start(); } if (!createFiles.isEmpty()) { auto *const cmd = new ChecksumCreateFilesCommand(createFiles, nullptr); cmd->start(); } } void KleopatraApplication::setIgnoreNewInstance(bool ignore) { d->ignoreNewInstance = ignore; } bool KleopatraApplication::ignoreNewInstance() const { return d->ignoreNewInstance; } diff --git a/src/selftest/enginecheck.cpp b/src/selftest/enginecheck.cpp index b3399f412..c429fc49e 100644 --- a/src/selftest/enginecheck.cpp +++ b/src/selftest/enginecheck.cpp @@ -1,216 +1,216 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/enginecheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "enginecheck.h" #include "utils/gnupg-helper.h" #include "implementation_p.h" #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include using namespace Kleo; using namespace Kleo::_detail; using namespace GpgME; static QString engine_name(GpgME::Engine eng) { static const char *engines[] = { "gpg", "gpgsm", "gpgconf" }; return QString::fromLatin1(engines[eng]); } static QString test_name(GpgME::Engine eng) { static const char *names[] = { - I18N_NOOP2("@title", "GPG (OpenPGP Backend) installation"), - I18N_NOOP2("@title", "GpgSM (S/MIME Backend) installation"), - I18N_NOOP2("@title", "GpgConf (Configuration) installation"), + I18NC_NOOP("@title", "GPG (OpenPGP Backend) installation"), + I18NC_NOOP("@title", "GpgSM (S/MIME Backend) installation"), + I18NC_NOOP("@title", "GpgConf (Configuration) installation"), }; return i18nc("@title", names[eng]); } namespace { class EngineCheck : public SelfTestImplementation { public: explicit EngineCheck(GpgME::Engine eng) : SelfTestImplementation(test_name(eng)) { runTest(eng); } void runTest(GpgME::Engine eng) { // First use the crypto config which is much faster because it is only // created once and then kept in memory. Only if the crypoconfig is // bad we check into the engine info. const auto conf = QGpgME::cryptoConfig(); if (conf && eng == GpgME::GpgEngine) { m_passed = true; return; } else if (conf) { const auto comp = conf->component(engine_name(eng)); if (comp) { m_passed = true; return; } } // Problem with the config. Try to get more details: const Error err = GpgME::checkEngine(eng); Q_ASSERT(!err.code() || err.code() == GPG_ERR_INV_ENGINE); m_passed = !err; if (m_passed) { return; } m_explaination = xi18nc("@info", "A problem was detected with the %1 backend.", engine_name(eng)); const EngineInfo ei = engineInfo(eng); if (ei.isNull()) { m_error = i18n("not supported"); m_explaination += xi18nc("@info", "It seems that the gpgme library was compiled without " "support for this backend."); m_proposedFix += xi18nc("@info", "Replace the gpgme library with a version compiled " "with %1 support.", engine_name(eng)); } else if (ei.fileName() && (!ei.version() || !strcmp(ei.version(), "1.0.0"))) { // GPGSM only got the ei.version() working with 1.0.0 so 1.0.0 is returned as // a fallback if the version could not be checked. We assume that it's not properly // installed in that case. m_error = i18n("not properly installed"); m_explaination += xi18nc("@info", "Backend %1 is not installed properly.", QFile::decodeName(ei.fileName())); m_proposedFix += xi18nc("@info", "Please check the output of %1 --version manually.", QFile::decodeName(ei.fileName())); } else if (ei.fileName() && ei.version() && ei.requiredVersion()) { m_error = i18n("too old"); m_explaination += xi18nc("@info", "Backend %1 is installed in version %2, " "but at least version %3 is required.", QFile::decodeName(ei.fileName()), QString::fromUtf8(ei.version()), QString::fromUtf8(ei.requiredVersion())); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(eng), QString::fromUtf8(ei.requiredVersion())); } else { m_error = m_explaination = i18n("unknown problem"); m_proposedFix += xi18nc("@info", "Make sure %1 is installed and " "in PATH.", engine_name(eng)); } } }; } std::shared_ptr Kleo::makeGpgEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgEngine)); } std::shared_ptr Kleo::makeGpgSmEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgSMEngine)); } std::shared_ptr Kleo::makeGpgConfEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgConfEngine)); } // // SelfTestImplementation (parts) // bool SelfTestImplementation::ensureEngineVersion(GpgME::Engine engine, int major, int minor, int patch) { const Error err = GpgME::checkEngine(engine); Q_ASSERT(!err || err.code() == GPG_ERR_INV_ENGINE); m_skipped = err || !engineIsVersion(major, minor, patch, engine); if (!m_skipped) { return true; } const char *version = GpgME::engineInfo(engine).version(); if (!err && version) { // properly installed, but too old m_explaination = xi18nc("@info", "%1 v%2.%3.%4 is required for this test, but only %5 is installed.", engine_name(engine), major, minor, patch, QString::fromUtf8(version)); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(engine), QStringLiteral("%1.%2.%3").arg(major).arg(minor).arg(patch)); } else { // not properly installed m_explaination = xi18nc("@info", "%1 is required for this test, but does not seem available." "See tests further up for more information.", engine_name(engine)); m_proposedFix = xi18nc("@info %1: test name", "See \"%1\" above.", test_name(engine)); } return false; } diff --git a/src/uiserver/assuanserverconnection.cpp b/src/uiserver/assuanserverconnection.cpp index 46c7dd8af..8e16e0fce 100644 --- a/src/uiserver/assuanserverconnection.cpp +++ b/src/uiserver/assuanserverconnection.cpp @@ -1,1716 +1,1717 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/assuanserverconnection.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef QT_NO_CAST_TO_ASCII # define QT_NO_CAST_TO_ASCII #endif #ifndef QT_NO_CAST_FROM_ASCII # define QT_NO_CAST_FROM_ASCII #endif #include #include #include "assuanserverconnection.h" #include "assuancommand.h" #include "sessiondata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBCXX__ # include // for is_sorted #endif #ifdef Q_OS_WIN32 # include # include #else # include # include #endif using namespace Kleo; static const unsigned int INIT_SOCKET_FLAGS = 3; // says info assuan... //static int(*USE_DEFAULT_HANDLER)(assuan_context_t,char*) = 0; static const int FOR_READING = 0; static const unsigned int MAX_ACTIVE_FDS = 32; #ifdef HAVE_ASSUAN2 static void my_assuan_release(assuan_context_t ctx) { if (ctx) { assuan_release(ctx); } } #endif // std::shared_ptr for assuan_context_t w/ deleter enforced to assuan_deinit_server: typedef std::shared_ptr::type> AssuanContextBase; struct AssuanContext : AssuanContextBase { AssuanContext() : AssuanContextBase() {} #ifndef HAVE_ASSUAN2 explicit AssuanContext(assuan_context_t ctx) : AssuanContextBase(ctx, &assuan_deinit_server) {} #else explicit AssuanContext(assuan_context_t ctx) : AssuanContextBase(ctx, &my_assuan_release) {} #endif #ifndef HAVE_ASSUAN2 void reset(assuan_context_t ctx = 0) { AssuanContextBase::reset(ctx, &assuan_deinit_server); } #else void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &my_assuan_release); } #endif }; static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const char *err_msg) { return assuan_process_done(ctx, assuan_set_error(ctx, err, err_msg)); } static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const std::string &err_msg) { return assuan_process_done_msg(ctx, err, err_msg.c_str()); } static inline gpg_error_t assuan_process_done_msg(assuan_context_t ctx, gpg_error_t err, const QString &err_msg) { return assuan_process_done_msg(ctx, err, err_msg.toUtf8().constData()); } static std::map upcase_option(const char *option, std::map options) { std::string value; bool value_found = false; std::map::iterator it = options.begin(); while (it != options.end()) if (qstricmp(it->first.c_str(), option) == 0) { value = it->second; options.erase(it++); value_found = true; } else { ++it; } if (value_found) { options[option] = value; } return options; } static std::map parse_commandline(const char *line) { std::map result; if (line) { const char *begin = line; const char *lastEQ = nullptr; while (*line) { if (*line == ' ' || *line == '\t') { if (begin != line) { if (begin[0] == '-' && begin[1] == '-') { begin += 2; // skip initial "--" } if (lastEQ && lastEQ > begin) { result[ std::string(begin, lastEQ - begin) ] = hexdecode(std::string(lastEQ + 1, line - (lastEQ + 1))); } else { result[ std::string(begin, line - begin) ] = std::string(); } } begin = line + 1; } else if (*line == '=') { if (line == begin) throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), i18n("No option name given")); else { lastEQ = line; } } ++line; } if (begin != line) { if (begin[0] == '-' && begin[1] == '-') { begin += 2; // skip initial "--" } if (lastEQ && lastEQ > begin) { result[ std::string(begin, lastEQ - begin) ] = hexdecode(std::string(lastEQ + 1, line - (lastEQ + 1))); } else { result[ begin ] = std::string(); } } } return result; } static WId wid_from_string(const QString &winIdStr, bool *ok = nullptr) { return static_cast(winIdStr.toULongLong(ok, 16)); } static void apply_window_id(QWidget *widget, const QString &winIdStr) { if (!widget || winIdStr.isEmpty()) { return; } bool ok = false; const WId wid = wid_from_string(winIdStr, &ok); if (!ok) { qCDebug(KLEOPATRA_LOG) << "window-id value" << wid << "doesn't look like a number"; return; } if (QWidget *pw = QWidget::find(wid)) { widget->setParent(pw, widget->windowFlags()); } else { - KWindowSystem::setMainWindow(widget, wid); + widget->setAttribute(Qt::WA_NativeWindow, true); + KWindowSystem::setMainWindow(widget->windowHandle(), wid); } } // // // AssuanServerConnection: // // class AssuanServerConnection::Private : public QObject { Q_OBJECT friend class ::Kleo::AssuanServerConnection; friend class ::Kleo::AssuanCommandFactory; friend class ::Kleo::AssuanCommand; AssuanServerConnection *const q; public: Private(assuan_fd_t fd_, const std::vector< std::shared_ptr > &factories_, AssuanServerConnection *qq); ~Private(); Q_SIGNALS: void startKeyManager(); public Q_SLOTS: void slotReadActivity(int) { Q_ASSERT(ctx); #ifndef HAVE_ASSUAN2 if (const int err = assuan_process_next(ctx.get())) { #else int done = false; if (const int err = assuan_process_next(ctx.get(), &done) || done) { #endif //if ( err == -1 || gpg_err_code(err) == GPG_ERR_EOF ) { topHalfDeletion(); if (nohupedCommands.empty()) { bottomHalfDeletion(); } //} else { //assuan_process_done( ctx.get(), err ); //return; //} } } int startCommandBottomHalf(); private: void nohupDone(AssuanCommand *cmd) { const auto it = std::find_if(nohupedCommands.begin(), nohupedCommands.end(), [cmd](const std::shared_ptr &other) { return other.get() == cmd; }); Q_ASSERT(it != nohupedCommands.end()); nohupedCommands.erase(it); if (nohupedCommands.empty() && closed) { bottomHalfDeletion(); } } void commandDone(AssuanCommand *cmd) { if (!cmd || cmd != currentCommand.get()) { return; } currentCommand.reset(); } void topHalfDeletion() { if (currentCommand) { currentCommand->canceled(); } if (fd != ASSUAN_INVALID_FD) { #if defined(Q_OS_WIN32) CloseHandle(fd); #else ::close(fd); #endif } notifiers.clear(); closed = true; } void bottomHalfDeletion() { if (sessionId) { SessionDataHandler::instance()->exitSession(sessionId); } cleanup(); const QPointer that = this; Q_EMIT q->closed(q); if (that) { // still there q->deleteLater(); } } private: #ifndef HAVE_ASSUAN2 static void reset_handler(assuan_context_t ctx_) { #else static gpg_error_t reset_handler(assuan_context_t ctx_, char *) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); conn.reset(); #ifdef HAVE_ASSUAN2 return 0; #endif } #ifndef HAVE_ASSUAN2 static int option_handler(assuan_context_t ctx_, const char *key, const char *value) { #else static gpg_error_t option_handler(assuan_context_t ctx_, const char *key, const char *value) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (key && key[0] == '-' && key[1] == '-') { key += 2; // skip "--" } conn.options[key] = QString::fromUtf8(value); return 0; //return gpg_error( GPG_ERR_UNKNOWN_OPTION ); } #ifndef HAVE_ASSUAN2 static int session_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t session_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); const QString str = QString::fromUtf8(line); QRegExp rx(QLatin1String("(\\d+)(?:\\s+(.*))?")); if (!rx.exactMatch(str)) { static const QString errorString = i18n("Parse error"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_SYNTAX), errorString); } bool ok = false; if (const qulonglong id = rx.cap(1).toULongLong(&ok)) { if (ok && id <= std::numeric_limits::max()) { SessionDataHandler::instance()->enterSession(id); conn.sessionId = id; } else { static const QString errorString = i18n("Parse error: numeric session id too large"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_SYNTAX), errorString); } } if (!rx.cap(2).isEmpty()) { conn.sessionTitle = rx.cap(2); } qCDebug(KLEOPATRA_LOG) << "session_handler: " << "id=" << static_cast(conn.sessionId) << ", title=" << qPrintable(conn.sessionTitle); return assuan_process_done(ctx_, 0); } #ifndef HAVE_ASSUAN2 static int capabilities_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t capabilities_handler(assuan_context_t ctx_, char *line) { #endif if (!QByteArray(line).trimmed().isEmpty()) { static const QString errorString = i18n("CAPABILITIES does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } static const char capabilities[] = "SENDER=info\n" "RECIPIENT=info\n" "SESSION\n" ; return assuan_process_done(ctx_, assuan_send_data(ctx_, capabilities, sizeof capabilities - 1)); } #ifndef HAVE_ASSUAN2 static int getinfo_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t getinfo_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (qstrcmp(line, "version") == 0) { static const char version[] = "Kleopatra " KLEOPATRA_VERSION_STRING; return assuan_process_done(ctx_, assuan_send_data(ctx_, version, sizeof version - 1)); } QByteArray ba; if (qstrcmp(line, "pid") == 0) { ba = QByteArray::number(QCoreApplication::applicationPid()); } else if (qstrcmp(line, "options") == 0) { ba = conn.dumpOptions(); } else if (qstrcmp(line, "x-mementos") == 0) { ba = conn.dumpMementos(); } else if (qstrcmp(line, "senders") == 0) { ba = conn.dumpSenders(); } else if (qstrcmp(line, "recipients") == 0) { ba = conn.dumpRecipients(); } else if (qstrcmp(line, "x-files") == 0) { ba = conn.dumpFiles(); } else { static const QString errorString = i18n("Unknown value for WHAT"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } return assuan_process_done(ctx_, assuan_send_data(ctx_, ba.constData(), ba.size())); } #ifndef HAVE_ASSUAN2 static int start_keymanager_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t start_keymanager_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (line && *line) { static const QString errorString = i18n("START_KEYMANAGER does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } Q_EMIT conn.q->startKeyManagerRequested(); return assuan_process_done(ctx_, 0); } #ifndef HAVE_ASSUAN2 static int start_confdialog_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t start_confdialog_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); if (line && *line) { static const QString errorString = i18n("START_CONFDIALOG does not take arguments"); return assuan_process_done_msg(ctx_, gpg_error(GPG_ERR_ASS_PARAMETER), errorString); } Q_EMIT conn.q->startConfigDialogRequested(); return assuan_process_done(ctx_, 0); } template struct Input_or_Output : std::conditional {}; // format: TAG (FD|FD=\d+|FILE=...) template #ifndef HAVE_ASSUAN2 static int IO_handler(assuan_context_t ctx_, char *line_, T_memptr which) { #else static gpg_error_t IO_handler(assuan_context_t ctx_, char *line_, T_memptr which) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); char *binOpt = strstr(line_, "--binary"); if (binOpt && !in) { /* Note there is also --armor and --base64 allowed but we don't need * to parse those because they are default. * We remove it here so that it is not parsed as an Option.*/ memset(binOpt, ' ', 8); } try { /*const*/ std::map options = upcase_option("FD", upcase_option("FILE", parse_commandline(line_))); if (options.size() < 1 || options.size() > 2) { throw gpg_error(GPG_ERR_ASS_SYNTAX); } std::shared_ptr< typename Input_or_Output::type > io; if (options.count("FD")) { if (options.count("FILE")) { throw gpg_error(GPG_ERR_CONFLICT); } assuan_fd_t fd = ASSUAN_INVALID_FD; const std::string fdstr = options["FD"]; if (fdstr.empty()) { if (const gpg_error_t err = assuan_receivefd(conn.ctx.get(), &fd)) { throw err; } } else { #if defined(Q_OS_WIN32) fd = (assuan_fd_t)std::stoi(fdstr); #else fd = std::stoi(fdstr); #endif } io = Input_or_Output::type::createFromPipeDevice(fd, in ? i18n("Message #%1", (conn.*which).size() + 1) : QString()); options.erase("FD"); } else if (options.count("FILE")) { if (options.count("FD")) { throw gpg_error(GPG_ERR_CONFLICT); } const QString filePath = QFile::decodeName(options["FILE"].c_str()); if (filePath.isEmpty()) { throw Exception(gpg_error(GPG_ERR_ASS_SYNTAX), i18n("Empty file path")); } const QFileInfo fi(filePath); if (!fi.isAbsolute()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only absolute file paths are allowed")); } if (!fi.isFile()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only files are allowed in INPUT/OUTPUT FILE")); } else { io = Input_or_Output::type::createFromFile(fi.absoluteFilePath(), true); } options.erase("FILE"); } else { throw gpg_error(GPG_ERR_ASS_PARAMETER); } if (options.size()) { throw gpg_error(GPG_ERR_UNKNOWN_OPTION); } (conn.*which).push_back(io); if (binOpt && !in) { Output *out = reinterpret_cast (io.get()); out->setBinaryOpt(true); qCDebug(KLEOPATRA_LOG) << "Configured output for binary data"; } qCDebug(KLEOPATRA_LOG) << "AssuanServerConnection: added" << io->label(); return assuan_process_done(conn.ctx.get(), 0); } catch (const GpgME::Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error().encodedError(), e.message().c_str()); } catch (const std::exception &) { return assuan_process_done(conn.ctx.get(), gpg_error(GPG_ERR_ASS_SYNTAX)); } catch (const gpg_error_t &e) { return assuan_process_done(conn.ctx.get(), e); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), "unknown exception caught"); } } #ifndef HAVE_ASSUAN2 static int input_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t input_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::inputs); } #ifndef HAVE_ASSUAN2 static int output_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t output_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::outputs); } #ifndef HAVE_ASSUAN2 static int message_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t message_handler(assuan_context_t ctx, char *line) { #endif return IO_handler(ctx, line, &Private::messages); } #ifndef HAVE_ASSUAN2 static int file_handler(assuan_context_t ctx_, char *line) { #else static gpg_error_t file_handler(assuan_context_t ctx_, char *line) { #endif Q_ASSERT(assuan_get_pointer(ctx_)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx_)); try { const QFileInfo fi(QFile::decodeName(hexdecode(line).c_str())); if (!fi.isAbsolute()) { throw Exception(gpg_error(GPG_ERR_INV_ARG), i18n("Only absolute file paths are allowed")); } if (!fi.exists()) { throw gpg_error(GPG_ERR_ENOENT); } if (!fi.isReadable() || (fi.isDir() && !fi.isExecutable())) { throw gpg_error(GPG_ERR_EPERM); } conn.files.push_back(fi.absoluteFilePath()); return assuan_process_done(conn.ctx.get(), 0); } catch (const Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error().encodedError(), e.message().toUtf8().constData()); } catch (const gpg_error_t &e) { return assuan_process_done(conn.ctx.get(), e); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("unknown exception caught").toUtf8().constData()); } } static bool parse_informative(const char *&begin, GpgME::Protocol &protocol) { protocol = GpgME::UnknownProtocol; bool informative = false; const char *pos = begin; while (true) { while (*pos == ' ' || *pos == '\t') { ++pos; } if (qstrnicmp(pos, "--info", strlen("--info")) == 0) { informative = true; pos += strlen("--info"); if (*pos == '=') { ++pos; break; } } else if (qstrnicmp(pos, "--protocol=", strlen("--protocol=")) == 0) { pos += strlen("--protocol="); if (qstrnicmp(pos, "OpenPGP", strlen("OpenPGP")) == 0) { protocol = GpgME::OpenPGP; pos += strlen("OpenPGP"); } else if (qstrnicmp(pos, "CMS", strlen("CMS")) == 0) { protocol = GpgME::CMS; pos += strlen("CMS"); } else { ; } } else if (qstrncmp(pos, "-- ", strlen("-- ")) == 0) { pos += 3; while (*pos == ' ' || *pos == '\t') { ++pos; } break; } else { break; } } begin = pos; return informative; } template #ifndef HAVE_ASSUAN2 static int recipient_sender_handler(T_memptr mp, T_memptr2 info, assuan_context_t ctx, char *line, bool sender = false) { #else static gpg_error_t recipient_sender_handler(T_memptr mp, T_memptr2 info, assuan_context_t ctx, char *line, bool sender = false) { #endif Q_ASSERT(assuan_get_pointer(ctx)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx)); if (!line || !*line) { return assuan_process_done(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG)); } const char *begin = line; const char *const end = begin + qstrlen(line); GpgME::Protocol proto = GpgME::UnknownProtocol; const bool informative = parse_informative(begin, proto); if (!(conn.*mp).empty() && informative != (conn.*info)) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_CONFLICT), i18n("Cannot mix --info with non-info SENDER or RECIPIENT").toUtf8().constData()); KMime::Types::Mailbox mb; if (!KMime::HeaderParsing::parseMailbox(begin, end, mb)) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG), i18n("Argument is not a valid RFC-2822 mailbox").toUtf8().constData()); if (begin != end) return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_INV_ARG), i18n("Garbage after valid RFC-2822 mailbox detected").toUtf8().constData()); (conn.*info) = informative; (conn.*mp).push_back(mb); const QString email = mb.addrSpec().asString(); (void)assuan_write_line(conn.ctx.get(), qPrintable(QString().sprintf("# ok, parsed as \"%s\"", qPrintable(email)))); if (sender && !informative) { return AssuanCommandFactory::_handle(conn.ctx.get(), line, "PREP_SIGN"); } else { return assuan_process_done(ctx, 0); } } #ifndef HAVE_ASSUAN2 static int recipient_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t recipient_handler(assuan_context_t ctx, char *line) { #endif return recipient_sender_handler(&Private::recipients, &Private::informativeRecipients, ctx, line); } #ifndef HAVE_ASSUAN2 static int sender_handler(assuan_context_t ctx, char *line) { #else static gpg_error_t sender_handler(assuan_context_t ctx, char *line) { #endif return recipient_sender_handler(&Private::senders, &Private::informativeSenders, ctx, line, true); } QByteArray dumpOptions() const { QByteArray result; for (std::map::const_iterator it = options.begin(), end = options.end(); it != end; ++it) { result += it->first.c_str() + it->second.toString().toUtf8() + '\n'; } return result; } static QByteArray dumpStringList(const QStringList &sl) { return sl.join(QLatin1Char('\n')).toUtf8(); } template static QByteArray dumpStringList(const T_container &c) { QStringList sl; std::copy(c.begin(), c.end(), std::back_inserter(sl)); return dumpStringList(sl); } template static QByteArray dumpMailboxes(const T_container &c) { QStringList sl; std::transform(c.begin(), c.end(), std::back_inserter(sl), [](typename T_container::const_reference val) { return val.prettyAddress(); }); return dumpStringList(sl); } QByteArray dumpSenders() const { return dumpMailboxes(senders); } QByteArray dumpRecipients() const { return dumpMailboxes(recipients); } QByteArray dumpMementos() const { QByteArray result; for (std::map< QByteArray, std::shared_ptr >::const_iterator it = mementos.begin(), end = mementos.end(); it != end; ++it) { char buf[2 + 2 * sizeof(void *) + 2]; sprintf(buf, "0x%p\n", (void *)it->second.get()); buf[sizeof(buf) - 1] = '\0'; result += it->first + QByteArray::fromRawData(buf, sizeof buf); } return result; } QByteArray dumpFiles() const { QStringList rv; rv.reserve(files.size()); std::copy(files.cbegin(), files.cend(), std::back_inserter(rv)); return dumpStringList(rv); } void cleanup(); void reset() { options.clear(); senders.clear(); informativeSenders = false; recipients.clear(); informativeRecipients = false; sessionTitle.clear(); sessionId = 0; mementos.clear(); files.clear(); std::for_each(inputs.begin(), inputs.end(), std::mem_fn(&Input::finalize)); inputs.clear(); std::for_each(outputs.begin(), outputs.end(), std::mem_fn(&Output::finalize)); outputs.clear(); std::for_each(messages.begin(), messages.end(), std::mem_fn(&Input::finalize)); messages.clear(); bias = GpgME::UnknownProtocol; } assuan_fd_t fd; AssuanContext ctx; bool closed : 1; bool cryptoCommandsEnabled : 1; bool commandWaitingForCryptoCommandsEnabled : 1; bool currentCommandIsNohup : 1; bool informativeSenders; // address taken, so no : 1 bool informativeRecipients; // address taken, so no : 1 GpgME::Protocol bias; QString sessionTitle; unsigned int sessionId; std::vector< std::shared_ptr > notifiers; std::vector< std::shared_ptr > factories; // sorted: _detail::ByName std::shared_ptr currentCommand; std::vector< std::shared_ptr > nohupedCommands; std::map options; std::vector senders, recipients; std::vector< std::shared_ptr > inputs, messages; std::vector< std::shared_ptr > outputs; std::vector files; std::map< QByteArray, std::shared_ptr > mementos; }; void AssuanServerConnection::Private::cleanup() { Q_ASSERT(nohupedCommands.empty()); reset(); currentCommand.reset(); currentCommandIsNohup = false; commandWaitingForCryptoCommandsEnabled = false; notifiers.clear(); ctx.reset(); fd = ASSUAN_INVALID_FD; } AssuanServerConnection::Private::Private(assuan_fd_t fd_, const std::vector< std::shared_ptr > &factories_, AssuanServerConnection *qq) : QObject(), q(qq), fd(fd_), closed(false), cryptoCommandsEnabled(false), commandWaitingForCryptoCommandsEnabled(false), currentCommandIsNohup(false), informativeSenders(false), informativeRecipients(false), bias(GpgME::UnknownProtocol), sessionId(0), factories(factories_) { #ifdef __GLIBCXX__ Q_ASSERT(__gnu_cxx::is_sorted(factories_.begin(), factories_.end(), _detail::ByName())); #endif if (fd == ASSUAN_INVALID_FD) { throw Exception(gpg_error(GPG_ERR_INV_ARG), "pre-assuan_init_socket_server_ext"); } #ifndef HAVE_ASSUAN2 assuan_context_t naked_ctx = 0; if (const gpg_error_t err = assuan_init_socket_server_ext(&naked_ctx, fd, INIT_SOCKET_FLAGS)) #else { assuan_context_t naked_ctx = nullptr; if (const gpg_error_t err = assuan_new(&naked_ctx)) { throw Exception(err, "assuan_new"); } ctx.reset(naked_ctx); } if (const gpg_error_t err = assuan_init_socket_server(ctx.get(), fd, INIT_SOCKET_FLAGS)) #endif throw Exception(err, "assuan_init_socket_server_ext"); #ifndef HAVE_ASSUAN2 ctx.reset(naked_ctx); naked_ctx = 0; #endif // for callbacks, associate the context with this connection: assuan_set_pointer(ctx.get(), this); FILE *const logFile = Log::instance()->logFile(); assuan_set_log_stream(ctx.get(), logFile ? logFile : stderr); // register FDs with the event loop: assuan_fd_t fds[MAX_ACTIVE_FDS]; const int numFDs = assuan_get_active_fds(ctx.get(), FOR_READING, fds, MAX_ACTIVE_FDS); Q_ASSERT(numFDs != -1); // == 1 if (!numFDs || fds[0] != fd) { const std::shared_ptr sn(new QSocketNotifier((intptr_t)fd, QSocketNotifier::Read), std::mem_fn(&QObject::deleteLater)); connect(sn.get(), &QSocketNotifier::activated, this, &Private::slotReadActivity); notifiers.push_back(sn); } notifiers.reserve(notifiers.size() + numFDs); for (int i = 0; i < numFDs; ++i) { const std::shared_ptr sn(new QSocketNotifier((intptr_t)fds[i], QSocketNotifier::Read), std::mem_fn(&QObject::deleteLater)); connect(sn.get(), &QSocketNotifier::activated, this, &Private::slotReadActivity); notifiers.push_back(sn); } // register our INPUT/OUTPUT/MESSGAE/FILE handlers: #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "INPUT", input_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "INPUT", input_handler, "")) #endif throw Exception(err, "register \"INPUT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "MESSAGE", message_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "MESSAGE", message_handler, "")) #endif throw Exception(err, "register \"MESSAGE\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "OUTPUT", output_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "OUTPUT", output_handler, "")) #endif throw Exception(err, "register \"OUTPUT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "FILE", file_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "FILE", file_handler, "")) #endif throw Exception(err, "register \"FILE\" handler"); // register user-defined commands: Q_FOREACH (std::shared_ptr fac, factories) #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), fac->name(), fac->_handler())) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), fac->name(), fac->_handler(), "")) #endif throw Exception(err, std::string("register \"") + fac->name() + "\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "GETINFO", getinfo_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "GETINFO", getinfo_handler, "")) #endif throw Exception(err, "register \"GETINFO\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_KEYMANAGER", start_keymanager_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_KEYMANAGER", start_keymanager_handler, "")) #endif throw Exception(err, "register \"START_KEYMANAGER\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_CONFDIALOG", start_confdialog_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "START_CONFDIALOG", start_confdialog_handler, "")) #endif throw Exception(err, "register \"START_CONFDIALOG\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "RECIPIENT", recipient_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "RECIPIENT", recipient_handler, "")) #endif throw Exception(err, "register \"RECIPIENT\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "SENDER", sender_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "SENDER", sender_handler, "")) #endif throw Exception(err, "register \"SENDER\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "SESSION", session_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "SESSION", session_handler, "")) #endif throw Exception(err, "register \"SESSION\" handler"); #ifndef HAVE_ASSUAN2 if (const gpg_error_t err = assuan_register_command(ctx.get(), "CAPABILITIES", capabilities_handler)) #else if (const gpg_error_t err = assuan_register_command(ctx.get(), "CAPABILITIES", capabilities_handler, "")) #endif throw Exception(err, "register \"CAPABILITIES\" handler"); assuan_set_hello_line(ctx.get(), "GPG UI server (Kleopatra/" KLEOPATRA_VERSION_STRING ") ready to serve"); //assuan_set_hello_line( ctx.get(), GPG UI server (qApp->applicationName() + " v" + kapp->applicationVersion() + "ready to serve" ) // some notifiers we're interested in: if (const gpg_error_t err = assuan_register_reset_notify(ctx.get(), reset_handler)) { throw Exception(err, "register reset notify"); } if (const gpg_error_t err = assuan_register_option_handler(ctx.get(), option_handler)) { throw Exception(err, "register option handler"); } // and last, we need to call assuan_accept, which doesn't block // (d/t INIT_SOCKET_FLAGS), but performs vital connection // establishing handling: if (const gpg_error_t err = assuan_accept(ctx.get())) { throw Exception(err, "assuan_accept"); } } AssuanServerConnection::Private::~Private() { cleanup(); } AssuanServerConnection::AssuanServerConnection(assuan_fd_t fd, const std::vector< std::shared_ptr > &factories, QObject *p) : QObject(p), d(new Private(fd, factories, this)) { } AssuanServerConnection::~AssuanServerConnection() {} void AssuanServerConnection::enableCryptoCommands(bool on) { if (on == d->cryptoCommandsEnabled) { return; } d->cryptoCommandsEnabled = on; if (d->commandWaitingForCryptoCommandsEnabled) { QTimer::singleShot(0, d.get(), &Private::startCommandBottomHalf); } } // // // AssuanCommand: // // namespace Kleo { class InquiryHandler : public QObject { Q_OBJECT public: #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) explicit InquiryHandler(const char *keyword_, QObject *p = nullptr) : QObject(p), # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) buffer(0), buflen(0), # endif keyword(keyword_) { } # if defined(HAVE_ASSUAN2) || defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) # ifndef HAVE_ASSUAN2 static int handler(void *cb_data, int rc, unsigned char *buffer, size_t buflen) # else static gpg_error_t handler(void *cb_data, gpg_error_t rc, unsigned char *buffer, size_t buflen) # endif { Q_ASSERT(cb_data); InquiryHandler *this_ = static_cast(cb_data); Q_EMIT this_->signal(rc, QByteArray::fromRawData(reinterpret_cast(buffer), buflen), this_->keyword); std::free(buffer); delete this_; return 0; } # else static int handler(void *cb_data, int rc) { Q_ASSERT(cb_data); InquiryHandler *this_ = static_cast(cb_data); Q_EMIT this_->signal(rc, QByteArray::fromRawData(reinterpret_cast(this_->buffer), this_->buflen), this_->keyword); std::free(this_->buffer); delete this_; return 0; } # endif private: #if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) friend class ::Kleo::AssuanCommand; unsigned char *buffer; size_t buflen; #endif const char *keyword; #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) Q_SIGNALS: void signal(int rc, const QByteArray &data, const QByteArray &keyword); }; } // namespace Kleo class AssuanCommand::Private { public: Private() : informativeRecipients(false), informativeSenders(false), bias(GpgME::UnknownProtocol), done(false), nohup(false) { } std::map options; std::vector< std::shared_ptr > inputs, messages; std::vector< std::shared_ptr > outputs; std::vector files; std::vector recipients, senders; bool informativeRecipients, informativeSenders; GpgME::Protocol bias; QString sessionTitle; unsigned int sessionId; QByteArray utf8ErrorKeepAlive; AssuanContext ctx; bool done; bool nohup; }; AssuanCommand::AssuanCommand() : d(new Private) { } AssuanCommand::~AssuanCommand() { } int AssuanCommand::start() { try { if (const int err = doStart()) if (!d->done) { done(err); } return 0; } catch (const Exception &e) { if (!d->done) { done(e.error_code(), e.message()); } return 0; } catch (const GpgME::Exception &e) { if (!d->done) { done(e.error(), QString::fromLocal8Bit(e.message().c_str())); } return 0; } catch (const std::exception &e) { if (!d->done) { done(makeError(GPG_ERR_INTERNAL), i18n("Caught unexpected exception: %1", QString::fromLocal8Bit(e.what()))); } return 0; } catch (...) { if (!d->done) { done(makeError(GPG_ERR_INTERNAL), i18n("Caught unknown exception - please report this error to the developers.")); } return 0; } } void AssuanCommand::canceled() { d->done = true; doCanceled(); } // static int AssuanCommand::makeError(int code) { return makeGnuPGError(code); } bool AssuanCommand::hasOption(const char *opt) const { return d->options.count(opt); } QVariant AssuanCommand::option(const char *opt) const { const std::map::const_iterator it = d->options.find(opt); if (it == d->options.end()) { return QVariant(); } else { return it->second; } } const std::map &AssuanCommand::options() const { return d->options; } namespace { template std::vector keys(const std::map &map) { std::vector result; result.resize(map.size()); for (typename std::map::const_iterator it = map.begin(), end = map.end(); it != end; ++it) { result.push_back(it->first); } return result; } } const std::map< QByteArray, std::shared_ptr > &AssuanCommand::mementos() const { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); const AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); return conn.mementos; } bool AssuanCommand::hasMemento(const QByteArray &tag) const { if (const unsigned int id = sessionId()) { return SessionDataHandler::instance()->sessionData(id)->mementos.count(tag) || mementos().count(tag); } else { return mementos().count(tag); } } std::shared_ptr AssuanCommand::memento(const QByteArray &tag) const { if (const unsigned int id = sessionId()) { const std::shared_ptr sdh = SessionDataHandler::instance(); const std::shared_ptr sd = sdh->sessionData(id); const std::map< QByteArray, std::shared_ptr >::const_iterator it = sd->mementos.find(tag); if (it != sd->mementos.end()) { return it->second; } } const std::map< QByteArray, std::shared_ptr >::const_iterator it = mementos().find(tag); if (it == mementos().end()) { return std::shared_ptr(); } else { return it->second; } } QByteArray AssuanCommand::registerMemento(const std::shared_ptr &mem) { const QByteArray tag = QByteArray::number(reinterpret_cast(mem.get()), 36); return registerMemento(tag, mem); } QByteArray AssuanCommand::registerMemento(const QByteArray &tag, const std::shared_ptr &mem) { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); if (const unsigned int id = sessionId()) { SessionDataHandler::instance()->sessionData(id)->mementos[tag] = mem; } else { conn.mementos[tag] = mem; } return tag; } void AssuanCommand::removeMemento(const QByteArray &tag) { // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); conn.mementos.erase(tag); if (const unsigned int id = sessionId()) { SessionDataHandler::instance()->sessionData(id)->mementos.erase(tag); } } const std::vector< std::shared_ptr > &AssuanCommand::inputs() const { return d->inputs; } const std::vector< std::shared_ptr > &AssuanCommand::messages() const { return d->messages; } const std::vector< std::shared_ptr > &AssuanCommand::outputs() const { return d->outputs; } QStringList AssuanCommand::fileNames() const { QStringList rv; rv.reserve(d->files.size()); std::copy(d->files.cbegin(), d->files.cend(), std::back_inserter(rv)); return rv; } unsigned int AssuanCommand::numFiles() const { return d->files.size(); } void AssuanCommand::sendStatus(const char *keyword, const QString &text) { sendStatusEncoded(keyword, text.toUtf8().constData()); } void AssuanCommand::sendStatusEncoded(const char *keyword, const std::string &text) { if (d->nohup) { return; } if (const int err = assuan_write_status(d->ctx.get(), keyword, text.c_str())) { throw Exception(err, i18n("Cannot send \"%1\" status", QString::fromLatin1(keyword))); } } void AssuanCommand::sendData(const QByteArray &data, bool moreToCome) { if (d->nohup) { return; } if (const gpg_error_t err = assuan_send_data(d->ctx.get(), data.constData(), data.size())) { throw Exception(err, i18n("Cannot send data")); } if (!moreToCome) if (const gpg_error_t err = assuan_send_data(d->ctx.get(), nullptr, 0)) { // flush throw Exception(err, i18n("Cannot flush data")); } } int AssuanCommand::inquire(const char *keyword, QObject *receiver, const char *slot, unsigned int maxSize) { Q_ASSERT(keyword); Q_ASSERT(receiver); Q_ASSERT(slot); if (d->nohup) { return makeError(GPG_ERR_INV_OP); } #if defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) std::unique_ptr ih(new InquiryHandler(keyword, receiver)); receiver->connect(ih.get(), SIGNAL(signal(int,QByteArray,QByteArray)), slot); if (const gpg_error_t err = assuan_inquire_ext(d->ctx.get(), keyword, # if !defined(HAVE_ASSUAN2) && !defined(HAVE_NEW_STYLE_ASSUAN_INQUIRE_EXT) &ih->buffer, &ih->buflen, # endif maxSize, InquiryHandler::handler, ih.get())) { return err; } ih.release(); return 0; #else return makeError(GPG_ERR_NOT_SUPPORTED); // libassuan too old #endif // defined(HAVE_ASSUAN2) || defined(HAVE_ASSUAN_INQUIRE_EXT) } void AssuanCommand::done(const GpgME::Error &err, const QString &details) { if (d->ctx && !d->done && !details.isEmpty()) { qCDebug(KLEOPATRA_LOG) << "Error: " << details; d->utf8ErrorKeepAlive = details.toUtf8(); if (!d->nohup) { assuan_set_error(d->ctx.get(), err.encodedError(), d->utf8ErrorKeepAlive.constData()); } } done(err); } void AssuanCommand::done(const GpgME::Error &err) { if (!d->ctx) { qCDebug(KLEOPATRA_LOG) << err.asString() << ": called with NULL ctx."; return; } if (d->done) { qCDebug(KLEOPATRA_LOG) << err.asString() << ": called twice!"; return; } d->done = true; std::for_each(d->messages.begin(), d->messages.end(), std::mem_fn(&Input::finalize)); std::for_each(d->inputs.begin(), d->inputs.end(), std::mem_fn(&Input::finalize)); std::for_each(d->outputs.begin(), d->outputs.end(), std::mem_fn(&Output::finalize)); d->messages.clear(); d->inputs.clear(); d->outputs.clear(); d->files.clear(); // oh, hack :( Q_ASSERT(assuan_get_pointer(d->ctx.get())); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(d->ctx.get())); if (d->nohup) { conn.nohupDone(this); return; } const gpg_error_t rc = assuan_process_done(d->ctx.get(), err.encodedError()); if (gpg_err_code(rc) != GPG_ERR_NO_ERROR) qFatal("AssuanCommand::done: assuan_process_done returned error %d (%s)", static_cast(rc), gpg_strerror(rc)); d->utf8ErrorKeepAlive.clear(); conn.commandDone(this); } void AssuanCommand::setNohup(bool nohup) { d->nohup = nohup; } bool AssuanCommand::isNohup() const { return d->nohup; } bool AssuanCommand::isDone() const { return d->done; } QString AssuanCommand::sessionTitle() const { return d->sessionTitle; } unsigned int AssuanCommand::sessionId() const { return d->sessionId; } bool AssuanCommand::informativeSenders() const { return d->informativeSenders; } bool AssuanCommand::informativeRecipients() const { return d->informativeRecipients; } const std::vector &AssuanCommand::recipients() const { return d->recipients; } const std::vector &AssuanCommand::senders() const { return d->senders; } #ifndef HAVE_ASSUAN2 int AssuanCommandFactory::_handle(assuan_context_t ctx, char *line, const char *commandName) { #else gpg_error_t AssuanCommandFactory::_handle(assuan_context_t ctx, char *line, const char *commandName) { #endif Q_ASSERT(assuan_get_pointer(ctx)); AssuanServerConnection::Private &conn = *static_cast(assuan_get_pointer(ctx)); try { const std::vector< std::shared_ptr >::const_iterator it = std::lower_bound(conn.factories.begin(), conn.factories.end(), commandName, _detail::ByName()); kleo_assert(it != conn.factories.end()); kleo_assert(*it); kleo_assert(qstricmp((*it)->name(), commandName) == 0); const std::shared_ptr cmd = (*it)->create(); kleo_assert(cmd); cmd->d->ctx = conn.ctx; cmd->d->options = conn.options; cmd->d->inputs.swap(conn.inputs); kleo_assert(conn.inputs.empty()); cmd->d->messages.swap(conn.messages); kleo_assert(conn.messages.empty()); cmd->d->outputs.swap(conn.outputs); kleo_assert(conn.outputs.empty()); cmd->d->files.swap(conn.files); kleo_assert(conn.files.empty()); cmd->d->senders.swap(conn.senders); kleo_assert(conn.senders.empty()); cmd->d->recipients.swap(conn.recipients); kleo_assert(conn.recipients.empty()); cmd->d->informativeRecipients = conn.informativeRecipients; cmd->d->informativeSenders = conn.informativeSenders; cmd->d->bias = conn.bias; cmd->d->sessionTitle = conn.sessionTitle; cmd->d->sessionId = conn.sessionId; const std::map cmdline_options = parse_commandline(line); for (std::map::const_iterator it = cmdline_options.begin(), end = cmdline_options.end(); it != end; ++it) { cmd->d->options[it->first] = QString::fromUtf8(it->second.c_str()); } bool nohup = false; if (cmd->d->options.count("nohup")) { if (!cmd->d->options["nohup"].toString().isEmpty()) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_ASS_PARAMETER), "--nohup takes no argument"); } nohup = true; cmd->d->options.erase("nohup"); } conn.currentCommand = cmd; conn.currentCommandIsNohup = nohup; QTimer::singleShot(0, &conn, &AssuanServerConnection::Private::startCommandBottomHalf); return 0; } catch (const Exception &e) { return assuan_process_done_msg(conn.ctx.get(), e.error_code(), e.message()); } catch (const std::exception &e) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), e.what()); } catch (...) { return assuan_process_done_msg(conn.ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception")); } } int AssuanServerConnection::Private::startCommandBottomHalf() { commandWaitingForCryptoCommandsEnabled = currentCommand && !cryptoCommandsEnabled; if (!cryptoCommandsEnabled) { return 0; } const std::shared_ptr cmd = currentCommand; if (!cmd) { return 0; } currentCommand.reset(); const bool nohup = currentCommandIsNohup; currentCommandIsNohup = false; try { if (const int err = cmd->start()) { if (cmd->isDone()) { return err; } else { return assuan_process_done(ctx.get(), err); } } if (cmd->isDone()) { return 0; } if (nohup) { cmd->setNohup(true); nohupedCommands.push_back(cmd); return assuan_process_done_msg(ctx.get(), 0, "Command put in the background to continue executing after connection end."); } else { currentCommand = cmd; return 0; } } catch (const Exception &e) { return assuan_process_done_msg(ctx.get(), e.error_code(), e.message()); } catch (const std::exception &e) { return assuan_process_done_msg(ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), e.what()); } catch (...) { return assuan_process_done_msg(ctx.get(), gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception")); } } // // // AssuanCommand convenience methods // // /*! Checks the \c --mode parameter. \returns The parameter as an AssuanCommand::Mode enum value. If no \c --mode was given, or it's value wasn't recognized, throws an Kleo::Exception. */ AssuanCommand::Mode AssuanCommand::checkMode() const { if (!hasOption("mode")) { throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("Required --mode option missing")); } const QString modeString = option("mode").toString().toLower(); if (modeString == QLatin1String("filemanager")) { return FileManager; } if (modeString == QLatin1String("email")) { return EMail; } throw Exception(makeError(GPG_ERR_INV_ARG), i18n("invalid mode: \"%1\"", modeString)); } /*! Checks the \c --protocol parameter. \returns The parameter as a GpgME::Protocol enum value. If \c --protocol was given, but has an invalid value, throws an Kleo::Exception. If no \c --protocol was given, checks the connection bias, if available, otherwise, in FileManager mode, returns GpgME::UnknownProtocol, but if \a mode == \c EMail, throws an Kleo::Exception instead. */ GpgME::Protocol AssuanCommand::checkProtocol(Mode mode, int options) const { if (!hasOption("protocol")) if (d->bias != GpgME::UnknownProtocol) { return d->bias; } else if (mode == AssuanCommand::EMail && (options & AllowProtocolMissing) == 0) { throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("Required --protocol option missing")); } else { return GpgME::UnknownProtocol; } else if (mode == AssuanCommand::FileManager) { throw Exception(makeError(GPG_ERR_INV_FLAG), i18n("--protocol is not allowed here")); } const QString protocolString = option("protocol").toString().toLower(); if (protocolString == QLatin1String("openpgp")) { return GpgME::OpenPGP; } if (protocolString == QLatin1String("cms")) { return GpgME::CMS; } throw Exception(makeError(GPG_ERR_INV_ARG), i18n("invalid protocol \"%1\"", protocolString)); } void AssuanCommand::doApplyWindowID(QWidget *widget) const { if (!widget || !hasOption("window-id")) { return; } apply_window_id(widget, option("window-id").toString()); } WId AssuanCommand::parentWId() const { return wid_from_string(option("window-id").toString()); } #include "assuanserverconnection.moc"