diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 514fbdf79..b1625e375 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -1,72 +1,10 @@ # assuan configure checks include(CheckFunctionExists) if ( ASSUAN2_FOUND ) else ( ASSUAN2_FOUND ) # TODO Clean this up with assuan 2 as hard dependency. message(FATAL_ERROR "At least version 2 of libassuan is required for Kleopatra.") endif ( ASSUAN2_FOUND ) -set( USABLE_ASSUAN_FOUND false ) - - - set( CMAKE_REQUIRED_INCLUDES ${ASSUAN2_INCLUDES} ) - - set( CMAKE_REQUIRED_LIBRARIES ${ASSUAN2_LIBRARIES} ) - set( USABLE_ASSUAN_FOUND true ) - - # TODO: this workaround will be removed as soon as we find better solution - if(MINGW) - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${KDEWIN32_INCLUDE_DIR}/mingw) - elseif(MSVC) - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${KDEWIN32_INCLUDE_DIR}/msvc) - endif(MINGW) - -if ( USABLE_ASSUAN_FOUND ) - # check if assuan.h can be compiled standalone (it couldn't, on - # Windows, until recently, because of a HAVE_W32_SYSTEM #ifdef in - # there) - check_cxx_source_compiles( " - #include - int main() { - return 1; - } - " - USABLE_ASSUAN_FOUND ) -endif( USABLE_ASSUAN_FOUND ) - -if ( USABLE_ASSUAN_FOUND ) - - # check whether assuan and gpgme may be linked to simultaneously - check_function_exists( "assuan_get_pointer" USABLE_ASSUAN_FOUND ) - -endif( USABLE_ASSUAN_FOUND ) - -if ( USABLE_ASSUAN_FOUND ) - - # check if gpg-error already has GPG_ERR_SOURCE_KLEO - check_cxx_source_compiles(" - #include - static gpg_err_source_t src = GPG_ERR_SOURCE_KLEO; - int main() { return 0; } - " - HAVE_GPG_ERR_SOURCE_KLEO ) - -endif ( USABLE_ASSUAN_FOUND ) - -if ( USABLE_ASSUAN_FOUND ) message( STATUS "Usable assuan found for Kleopatra" ) -else ( USABLE_ASSUAN_FOUND ) - message( STATUS "NO usable assuan found for Kleopatra" ) -endif ( USABLE_ASSUAN_FOUND ) - -if (USABLE_ASSUAN_FOUND) - set (HAVE_USABLE_ASSUAN 1) - set (HAVE_KLEOPATRACLIENT_LIBRARY 1) -else() - set (HAVE_USABLE_ASSUAN 0) - set (HAVE_KLEOPATRACLIENT_LIBRARY 0) -endif() - -set(CMAKE_REQUIRED_INCLUDES) -set(CMAKE_REQUIRED_LIBRARIES) diff --git a/config-kleopatra.h.cmake b/config-kleopatra.h.cmake index e2439bc32..aa2d3dd27 100644 --- a/config-kleopatra.h.cmake +++ b/config-kleopatra.h.cmake @@ -1,47 +1,41 @@ -/* Define to 1 if you have a recent enough libassuan */ -#cmakedefine HAVE_USABLE_ASSUAN 1 - -/* Define to 1 if you build libkleopatraclient */ -#cmakedefine HAVE_KLEOPATRACLIENT_LIBRARY 1 - /* DBus available */ #cmakedefine01 HAVE_QDBUS /* Defined if QGpgME supports changing the expiration date of the primary key and the subkeys simultaneously */ #cmakedefine QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1 /* Defined if QGpgME supports retrieving the default value of a config entry */ #cmakedefine QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1 /* Defined if QGpgME supports WKD lookup */ #cmakedefine QGPGME_SUPPORTS_WKDLOOKUP 1 /* Defined if QGpgME supports specifying an import filter when importing keys */ #cmakedefine QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1 /* Defined if QGpgME supports setting key origin when importing keys */ #cmakedefine QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1 /* Defined if QGpgME supports the export of secret keys */ #cmakedefine QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1 /* Defined if QGpgME supports the export of secret subkeys */ #cmakedefine QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1 /* Defined if QGpgME supports receiving keys by their key ids */ #cmakedefine QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1 /* Defined if QGpgME supports revoking own OpenPGP keys */ #cmakedefine QGPGME_SUPPORTS_KEY_REVOCATION 1 /* Defined if QGpgME supports refreshing keys */ #cmakedefine QGPGME_SUPPORTS_KEY_REFRESH 1 /* Defined if QGpgME supports setting the file name of encrypted data */ #cmakedefine QGPGME_SUPPORTS_SET_FILENAME 1 /* Defined if QGpgME supports setting the primary user id of a key */ #cmakedefine QGPGME_SUPPORTS_SET_PRIMARY_UID 1 /* Defined if GpgME++ supports setting the curve when generating ECC card keys */ #cmakedefine GPGMEPP_SUPPORTS_SET_CURVE 1 diff --git a/src/commands/selftestcommand.cpp b/src/commands/selftestcommand.cpp index e0d6a0a58..71747427b 100644 --- a/src/commands/selftestcommand.cpp +++ b/src/commands/selftestcommand.cpp @@ -1,268 +1,264 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/selftestcommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "selftestcommand.h" #include "command_p.h" #include #include "kleopatra_debug.h" #ifdef Q_OS_WIN # include #endif #include #include -#ifdef HAVE_KLEOPATRACLIENT_LIBRARY -# include -#endif +#include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; static const char *const components[] = { nullptr, // gpgconf "gpg", "gpg-agent", "scdaemon", "gpgsm", "dirmngr", }; static const unsigned int numComponents = sizeof components / sizeof * components; class SelfTestCommand::Private : Command::Private { friend class ::Kleo::Commands::SelfTestCommand; SelfTestCommand *q_func() const { return static_cast(q); } public: explicit Private(SelfTestCommand *qq, KeyListController *c); ~Private() override; private: void init(); void ensureDialogCreated() { if (dialog) { return; } dialog = new SelfTestDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &SelfTestDialog::updateRequested, q_func(), [this]() { slotUpdateRequested(); }); connect(dialog, &QDialog::accepted, q_func(), [this]() { slotDialogAccepted(); }); connect(dialog, &QDialog::rejected, q_func(), [this]() { slotDialogRejected(); }); dialog->setRunAtStartUp(runAtStartUp()); dialog->setAutomaticMode(automatic); } void ensureDialogShown() { ensureDialogCreated(); if (dialog->isVisible()) { dialog->raise(); } else { dialog->show(); } } bool runAtStartUp() const { const KConfigGroup config(KSharedConfig::openConfig(), "Self-Test"); return config.readEntry("run-at-startup", true); } void setRunAtStartUp(bool on) { KConfigGroup config(KSharedConfig::openConfig(), "Self-Test"); config.writeEntry("run-at-startup", on); } void runTests() { std::vector< std::shared_ptr > tests; #if defined(Q_OS_WIN) qCDebug(KLEOPATRA_LOG) << "Checking Windows Registry..."; tests.push_back(makeGpgProgramRegistryCheckSelfTest()); -#if defined(HAVE_KLEOPATRACLIENT_LIBRARY) qCDebug(KLEOPATRA_LOG) << "Checking Ui Server connectivity..."; tests.push_back(makeUiServerConnectivitySelfTest()); -#endif #endif qCDebug(KLEOPATRA_LOG) << "Checking gpg installation..."; tests.push_back(makeGpgEngineCheckSelfTest()); qCDebug(KLEOPATRA_LOG) << "Checking gpgsm installation..."; tests.push_back(makeGpgSmEngineCheckSelfTest()); qCDebug(KLEOPATRA_LOG) << "Checking gpgconf installation..."; tests.push_back(makeGpgConfEngineCheckSelfTest()); for (unsigned int i = 0; i < numComponents; ++i) { qCDebug(KLEOPATRA_LOG) << "Checking configuration of:" << components[i]; tests.push_back(makeGpgConfCheckConfigurationSelfTest(components[i])); } #ifndef Q_OS_WIN tests.push_back(makeGpgAgentConnectivitySelfTest()); #endif tests.push_back(makeDeVSComplianceCheckSelfTest()); tests.push_back(makeLibKleopatraRcSelfTest()); if (!dialog && std::none_of(tests.cbegin(), tests.cend(), [](const std::shared_ptr &test) { return test->failed(); })) { finished(); return; } ensureDialogCreated(); dialog->setTests(tests); ensureDialogShown(); } private: void slotDialogAccepted() { setRunAtStartUp(dialog->runAtStartUp()); finished(); } void slotDialogRejected() { if (automatic) { canceled = true; Command::Private::canceled(); } else { slotDialogAccepted(); } } void slotUpdateRequested() { const auto conf = QGpgME::cryptoConfig(); if (conf) { conf->clear(); } runTests(); } private: QPointer dialog; bool canceled; bool automatic; }; SelfTestCommand::Private *SelfTestCommand::d_func() { return static_cast(d.get()); } const SelfTestCommand::Private *SelfTestCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() SelfTestCommand::Private::Private(SelfTestCommand *qq, KeyListController *c) : Command::Private(qq, c), dialog(), canceled(false), automatic(false) { } SelfTestCommand::Private::~Private() { } SelfTestCommand::SelfTestCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } SelfTestCommand::SelfTestCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } void SelfTestCommand::Private::init() { } SelfTestCommand::~SelfTestCommand() {} void SelfTestCommand::setAutomaticMode(bool on) { d->automatic = on; if (d->dialog) { d->dialog->setAutomaticMode(on); } } bool SelfTestCommand::isCanceled() const { return d->canceled; } void SelfTestCommand::doStart() { if (d->automatic) { if (!d->runAtStartUp()) { d->finished(); return; } } else { d->ensureDialogCreated(); } d->runTests(); } void SelfTestCommand::doCancel() { d->canceled = true; if (d->dialog) { d->dialog->close(); } d->dialog = nullptr; } #undef d #undef q #include "moc_selftestcommand.cpp" diff --git a/src/kleo-assuan.h b/src/kleo-assuan.h index 700eb9046..9d9838ba0 100644 --- a/src/kleo-assuan.h +++ b/src/kleo-assuan.h @@ -1,42 +1,13 @@ /* -*- mode: c++; c-basic-offset:4 -*- kleo-assuan.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once -#ifdef HAVE_USABLE_ASSUAN -# include -#else -/* - * copied from assuan.h: - */ - -/* assuan.h - Definitions for the Assuan IPC library - * SPDX-FileCopyrightText: 2001, 2002, 2003, 2005, 2007 Free Software Foundation Inc. - * - * This file is part of Assuan. - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifdef _WIN32 -typedef void *assuan_fd_t; -#define ASSUAN_INVALID_FD ((void*)(-1)) -#define ASSUAN_INT2FD(s) ((void *)(s)) -#define ASSUAN_FD2INT(h) ((unsigned int)(h)) -#else -using assuan_fd_t = int; -#define ASSUAN_INVALID_FD (-1) -#define ASSUAN_INT2FD(s) ((s)) -#define ASSUAN_FD2INT(h) ((h)) -#endif -/* - * end copied from assuan.h - */ -#endif +#include diff --git a/src/kleopatra_options.h b/src/kleopatra_options.h index 488f5bb62..65beae9eb 100644 --- a/src/kleopatra_options.h +++ b/src/kleopatra_options.h @@ -1,59 +1,57 @@ /* kleopatra_options.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2015 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include static void kleopatra_options(QCommandLineParser *parser) { parser->addOptions({ QCommandLineOption({QStringLiteral("openpgp"), QStringLiteral("p")}, i18n("Use OpenPGP for the following operation")), QCommandLineOption({QStringLiteral("cms"), QStringLiteral("c")}, i18n("Use CMS (X.509, S/MIME) for the following operation")), -#ifdef HAVE_USABLE_ASSUAN QCommandLineOption(QStringLiteral("uiserver-socket"), i18n("Location of the socket the ui server is listening on"), QStringLiteral("argument")), QCommandLineOption(QStringLiteral("daemon"), i18n("Run UI server only, hide main window")), -#endif QCommandLineOption({QStringLiteral("import-certificate"), QStringLiteral("i")}, i18n("Import certificate file(s)")), QCommandLineOption({QStringLiteral("encrypt"), QStringLiteral("e")}, i18n("Encrypt file(s)")), QCommandLineOption({QStringLiteral("sign"), QStringLiteral("s")}, i18n("Sign file(s)")), QCommandLineOption({QStringLiteral("sign-encrypt"), QStringLiteral("E")}, i18n("Sign and/or encrypt file(s)")), QCommandLineOption(QStringLiteral("encrypt-sign"), i18n("Same as --sign-encrypt, do not use")), QCommandLineOption({QStringLiteral("decrypt"), QStringLiteral("d")}, i18n("Decrypt file(s)")), QCommandLineOption({QStringLiteral("verify"), QStringLiteral("V")}, i18n("Verify file/signature")), QCommandLineOption({QStringLiteral("decrypt-verify"), QStringLiteral("D")}, i18n("Decrypt and/or verify file(s)")), QCommandLineOption(QStringLiteral("search"), i18n("Search for a certificate on a keyserver")), QCommandLineOption(QStringLiteral("checksum"), i18n("Create or check a checksum file")), QCommandLineOption({QStringLiteral("query"), QStringLiteral("q")}, i18nc("If a certificate is already known it shows the certificate details dialog. " "Otherwise it brings up the certificate search dialog.", "Show details of a local certificate or search for it on a keyserver by fingerprint")), QCommandLineOption(QStringLiteral("gen-key"), i18n("Create a new key pair or certificate signing request")), QCommandLineOption(QStringLiteral("parent-windowid"), i18n("Parent Window Id for dialogs"), QStringLiteral("windowId")), QCommandLineOption(QStringLiteral("config"), i18n("Open the config dialog")), }); /* Security note: To avoid code execution by shared library injection * through e.g. -platformpluginpath any external input should be seperated * by a double dash -- this is why query / search uses positional arguments. * * For example on Windows there is an URLhandler for openpgp4fpr: * be opened with Kleopatra's query function. And while a browser should * urlescape such a query there might be tricks to inject a quote character * and as such inject command line options for Kleopatra in an URL. */ parser->addPositionalArgument(QStringLiteral("files"), i18n("File(s) to process"), QStringLiteral("-- [files..]")); parser->addPositionalArgument(QStringLiteral("query"), i18n("String or Fingerprint for query and search"), QStringLiteral("-- [query..]")); } diff --git a/src/kleopatraapplication.cpp b/src/kleopatraapplication.cpp index 8f911d4b5..c922e2be1 100644 --- a/src/kleopatraapplication.cpp +++ b/src/kleopatraapplication.cpp @@ -1,833 +1,829 @@ /* kleopatraapplication.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "kleopatraapplication.h" #include "mainwindow.h" #include "kleopatra_options.h" #include "systrayicon.h" #include "settings.h" #include "smimevalidationpreferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifdef HAVE_USABLE_ASSUAN -# include -#endif +#include #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/newcertificatesigningrequestcommand.h" #include "commands/newopenpgpcertificatecommand.h" #include "dialogs/updatenotification.h" #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #if QT_CONFIG(graphicseffect) #include #endif #include #include #include #include #include #ifdef Q_OS_WIN #include #endif 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; } namespace { class FocusFrame : public QFocusFrame { Q_OBJECT public: using QFocusFrame::QFocusFrame; protected: void paintEvent(QPaintEvent *event) override; }; static QRect effectiveWidgetRect(const QWidget *w) { // based on QWidgetPrivate::effectiveRectFor #if QT_CONFIG(graphicseffect) if (auto graphicsEffect = w->graphicsEffect(); graphicsEffect && graphicsEffect->isEnabled()) return graphicsEffect->boundingRectFor(w->rect()).toAlignedRect(); #endif // QT_CONFIG(graphicseffect) return w->rect(); } static QRect clipRect(const QWidget *w) { // based on QWidgetPrivate::clipRect if (!w->isVisible()) { return QRect(); } QRect r = effectiveWidgetRect(w); int ox = 0; int oy = 0; while (w && w->isVisible() && !w->isWindow() && w->parentWidget()) { ox -= w->x(); oy -= w->y(); w = w->parentWidget(); r &= QRect(ox, oy, w->width(), w->height()); } return r; } void FocusFrame::paintEvent(QPaintEvent *) { if (!widget()) { return; } QStylePainter p(this); QStyleOptionFocusRect option; initStyleOption(&option); const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option); const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option); const QRect rect = clipRect(widget()).adjusted(0, 0, hmargin*2, vmargin*2); p.setClipRect(rect); p.drawPrimitive(QStyle::PE_FrameFocusRect, option); } } class KleopatraApplication::Private { friend class ::KleopatraApplication; KleopatraApplication *const q; public: explicit Private(KleopatraApplication *qq) : q(qq) , ignoreNewInstance(true) , firstNewInstance(true) , sysTray(nullptr) , groupConfig{std::make_shared(QStringLiteral("kleopatragroupsrc"))} { } ~Private() { #ifndef QT_NO_SYSTEMTRAYICON delete sysTray; #endif } void setUpSysTrayIcon() { KDAB_SET_OBJECT_NAME(readerStatus); #ifndef QT_NO_SYSTEMTRAYICON sysTray = new SysTrayIcon(); sysTray->setFirstCardWithNullPin(readerStatus.firstCardWithNullPin()); sysTray->setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys()); connect(&readerStatus, &SmartCard::ReaderStatus::firstCardWithNullPinChanged, sysTray, &SysTrayIcon::setFirstCardWithNullPin); connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged, sysTray, &SysTrayIcon::setAnyCardCanLearnKeys); #endif } private: void connectConfigureDialog() { if (configureDialog) { if (q->mainWindow()) { connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } connect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); } } void disconnectConfigureDialog() { if (configureDialog) { if (q->mainWindow()) { disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } disconnect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); } } public: bool ignoreNewInstance; bool firstNewInstance; QPointer focusFrame; QPointer configureDialog; QPointer mainWindow; SmartCard::ReaderStatus readerStatus; #ifndef QT_NO_SYSTEMTRAYICON SysTrayIcon *sysTray; #endif std::shared_ptr groupConfig; std::shared_ptr keyCache; std::shared_ptr log; std::shared_ptr watcher; public: void setupKeyCache() { keyCache = KeyCache::mutableInstance(); keyCache->setRefreshInterval(SMimeValidationPreferences{}.refreshInterval()); watcher.reset(new FileSystemWatcher); watcher->whitelistFiles(gnupgFileWhitelist()); watcher->addPaths(gnupgFolderWhitelist()); watcher->setDelay(1000); keyCache->addFileSystemWatcher(watcher); keyCache->setGroupConfig(groupConfig); keyCache->setGroupsEnabled(Settings().groupsEnabled()); } void setUpFilterManager() { if (!Settings{}.cmsEnabled()) { KeyFilterManager::instance()->alwaysFilterByProtocol(GpgME::OpenPGP); } } 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 } void updateFocusFrame(QWidget *focusWidget) { if (focusWidget && focusWidget->inherits("QLabel") && focusWidget->window()->testAttribute(Qt::WA_KeyboardFocusChange)) { if (!focusFrame) { focusFrame = new FocusFrame{focusWidget}; } focusFrame->setWidget(focusWidget); } else if (focusFrame) { focusFrame->setWidget(nullptr); } } }; KleopatraApplication::KleopatraApplication(int &argc, char *argv[]) : QApplication(argc, argv), d(new Private(this)) { // disable parent<->child navigation in tree views with left/right arrow keys // because this interferes with column by column navigation that is required // for accessibility setStyleSheet(QStringLiteral("QTreeView { arrow-keys-navigate-into-children: 0; }")); connect(this, &QApplication::focusChanged, this, [this](QWidget *, QWidget *now) { d->updateFocusFrame(now); }); } void KleopatraApplication::init() { #ifdef Q_OS_WIN QWindowsWindowFunctions::setWindowActivationBehavior( QWindowsWindowFunctions::AlwaysActivateWindow); #endif const auto blockedUrlSchemes = Settings{}.blockedUrlSchemes(); for (const auto &scheme : blockedUrlSchemes) { QDesktopServices::setUrlHandler(scheme, this, "blockUrl"); } add_resources(); DN::setAttributeOrder(Settings{}.attributeOrder()); /* Start the gpg-agent early, this is done explicitly * because on an empty keyring our keylistings wont start * the agent. In that case any assuan-connect calls to * the agent will fail. The requested start via the * connection is additionally done in case the gpg-agent * is killed while Kleopatra is running. */ startGpgAgent(); connect(&d->readerStatus, &SmartCard::ReaderStatus::startOfGpgAgentRequested, this, &KleopatraApplication::startGpgAgent); d->setupKeyCache(); d->setUpSysTrayIcon(); d->setUpFilterManager(); d->setupLogging(); #ifdef Q_OS_WIN // Under Linux the Theme is set outside. On Windows we have to do // it ourself. if (SystemInfo::isDarkModeActive()) { QIcon::setThemeName("breeze-dark"); } #endif #ifndef QT_NO_SYSTEMTRAYICON d->sysTray->show(); #endif setQuitOnLastWindowClosed(false); KWindowSystem::allowExternalProcessWindowActivation(); } KleopatraApplication::~KleopatraApplication() { // main window doesn't receive "close" signal and cannot // save settings before app exit delete d->mainWindow; // work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514 KSharedConfig::openConfig()->sync(); } namespace { using Func = void (KleopatraApplication::*)(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::error(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); bool queryMode = parser.isSet(QStringLiteral("query")) || parser.isSet(QStringLiteral("search")); // Query and Search treat positional arguments differently, see below. if (!queryMode) { const auto positionalArguments = parser.positionalArguments(); for (const QString &file : 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 (queryMode) { needle = parser.positionalArguments().join(QLatin1Char(' ')); } 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"); } auto 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"))) { if (protocol == GpgME::CMS) { const Kleo::Settings settings{}; if (settings.cmsEnabled() && settings.cmsCertificateCreationAllowed()) { auto cmd = new NewCertificateSigningRequestCommand; cmd->setParentWId(parentId); cmd->start(); } else { return i18n("You are not allowed to create S/MIME certificate signing requests."); } } else { auto cmd = new NewOpenPGPCertificateCommand; cmd->setParentWId(parentId); cmd->start(); } return QString(); } // Check for --config command if (parser.isSet(QStringLiteral("config"))) { openConfigDialogWithForeignParent(parentId); return QString(); } struct FuncInfo { QString optionName; Func func; }; static const std::vector 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; Func foundFunc = nullptr; for (const auto &[opt, fn] : funcMap) { if (parser.isSet(opt) && found.isEmpty()) { found = opt; foundFunc = fn; } else if (parser.isSet(opt)) { return i18n(R"(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->*foundFunc)(files, protocol); } else { if (files.empty()) { if (!(d->firstNewInstance && isSessionRestored())) { qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow"; openOrRaiseMainWindow(); } } else { for (const QString& fileName : std::as_const(files)) { QFileInfo fi(fileName); if (!fi.isReadable()) { errors << i18n("Cannot read \"%1\"", fileName); } } const QVector allCmds = Command::commandsForFiles(files); for (Command *cmd : allCmds) { 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) { #ifdef Q_OS_WIN if (w->isMinimized()) { qCDebug(KLEOPATRA_LOG) << __func__ << "unminimizing and raising window"; KWindowSystem::unminimizeWindow(w->winId()); w->raise(); } else if (w->isVisible()) { qCDebug(KLEOPATRA_LOG) << __func__ << "raising window"; w->raise(); #else if (w->isVisible()) { qCDebug(KLEOPATRA_LOG) << __func__ << "activating window"; KWindowSystem::updateStartupId(w->windowHandle()); KWindowSystem::activateWindow(w->windowHandle()); #endif } else { qCDebug(KLEOPATRA_LOG) << __func__ << "showing window"; 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; } auto 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 { d->configureDialog->setAttribute(Qt::WA_NativeWindow, true); 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) { auto 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) { auto 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) { auto const cmd = new SignEncryptFilesCommand(files, nullptr); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Decrypt); cmd->start(); } void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Verify); cmd->start(); } void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto 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; } void KleopatraApplication::blockUrl(const QUrl &url) { qCDebug(KLEOPATRA_LOG) << "Blocking URL" << url; KMessageBox::error(mainWindow(),i18n ("Opening an external link is administratively prohibited."), i18n ("Prohibited")); } void KleopatraApplication::startGpgAgent() { Kleo::launchGpgAgent(); } #include "kleopatraapplication.moc" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 182921fa2..cbb7d3aaf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,79 +1,76 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(gnupg_home) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) include_directories( ${CMAKE_SOURCE_DIR}/src/ ${CMAKE_BINARY_DIR}/src/ ${GPGME_INCLUDES} ) ########### next target ############### set(test_verify_SRCS test_verify.cpp) add_definitions(-DKLEO_TEST_GNUPGHOME="${CMAKE_CURRENT_BINARY_DIR}/gnupg_home") add_definitions(-DKLEO_TEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}") add_executable(test_verify ${test_verify_SRCS}) add_test(NAME test_verify COMMAND test_verify) ecm_mark_as_test(test_verify) target_link_libraries(test_verify KF5::Libkleo Qt::Test KF5::CoreAddons KF5::I18n Qt::Widgets ) if (QT_MAJOR_VERSION STREQUAL "6") target_link_libraries(test_verify QGpgmeQt6) else() target_link_libraries(test_verify QGpgme) endif() ########### next target ############### -if(USABLE_ASSUAN_FOUND) - # this doesn't yet work on Windows add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_2) set(test_uiserver_SRCS test_uiserver.cpp ${CMAKE_SOURCE_DIR}/src/utils/wsastarter.cpp) #FIXME: omitting TEST makes test_uiserver print output again on a Win32 console; # find a better fix for this issue if(WIN32) add_executable(test_uiserver ${test_uiserver_SRCS}) else() add_executable(test_uiserver ${test_uiserver_SRCS}) endif() target_link_libraries(test_uiserver KF5::I18n) target_link_libraries(test_uiserver KF5::Libkleo ${ASSUAN2_LIBRARIES} ) if(WIN32) target_link_libraries(test_uiserver ${ASSUAN_VANILLA_LIBRARIES} ws2_32 ) else() target_link_libraries(test_uiserver ${ASSUAN_PTHREAD_LIBRARIES} ) endif() if (QT_MAJOR_VERSION STREQUAL "6") target_link_libraries(test_uiserver QGpgmeQt6) else() target_link_libraries(test_uiserver QGpgme) endif() -endif()