diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cc2d7e33..0dae81c7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,282 +1,285 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) set(RELEASE_SERVICE_VERSION_MAJOR "23") set(RELEASE_SERVICE_VERSION_MINOR "07") set(RELEASE_SERVICE_VERSION_MICRO "70") # The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version if (NOT RELEASE_SERVICE_VERSION) set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") endif() if(RELEASE_SERVICE_VERSION_MICRO LESS 10) set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}0${RELEASE_SERVICE_VERSION_MICRO}") else() set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}${RELEASE_SERVICE_VERSION_MICRO}") endif() set(KLEOPATRA_VERSION_MAJOR "3") set(KLEOPATRA_VERSION_MINOR "1") set(KLEOPATRA_VERSION_MICRO "26") set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}.${KDE_APPLICATIONS_COMPACT_VERSION}") # The following is for Windows set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}") set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_MICRO},0") if (NOT KLEOPATRA_DISTRIBUTION_TEXT) # This is only used on Windows for the file attributes of Kleopatra set(KLEOPATRA_DISTRIBUTION_TEXT "KDE") endif() project(kleopatra VERSION ${kleopatra_version}) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF_MIN_VERSION "5.105.0") set(KIDENTITYMANAGEMENT_VERSION "5.23.40") set(KMAILTRANSPORT_VERSION "5.23.40") set(AKONADI_MIME_VERSION "5.23.41") set(KMIME_VERSION "5.23.40") set(LIBKLEO_VERSION "5.23.43") set(QT_REQUIRED_VERSION "5.15.2") if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") endif() set(GPGME_REQUIRED_VERSION "1.16.0") set(LIBASSUAN_REQUIRED_VERSION "2.4.2") set(GPG_ERROR_REQUIRED_VERSION "1.36") if (WIN32) set(KF5_WANT_VERSION "5.70.0") set(KMIME_WANT_VERSION "5.12.0") else () set(KF5_WANT_VERSION ${KF_MIN_VERSION}) set(KMIME_WANT_VERSION ${KMIME_VERSION}) endif () set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_WANT_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(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) include(ECMDeprecationSettings) include(KDEClangFormat) if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") set(KF_MIN_VERSION "5.240.0") set(KF_MAJOR_VERSION "6") else() set(KF_MIN_VERSION "5.105.0") set(KF_MAJOR_VERSION "5") endif() # Find KF5 packages find_package(KF${KF_MAJOR_VERSION} ${KF5_WANT_VERSION} REQUIRED COMPONENTS Codecs Config ConfigWidgets CoreAddons Crash I18n IconThemes ItemModels KCMUtils KIO WidgetsAddons WindowSystem XmlGui OPTIONAL_COMPONENTS DocTools ) set_package_properties(KF${KF_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION "Documentation tools" PURPOSE "Required to generate Kleopatra documentation." TYPE OPTIONAL) # Optional packages if (WIN32) # Only a replacement available for Windows so this # is required on other platforms. find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG) set_package_properties(KF${KF_MAJOR_VERSION}DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus" PURPOSE "DBus session integration" URL "https://inqlude.org/libraries/kdbusaddons.html" TYPE OPTIONAL) else() find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) set(_kleopatra_dbusaddons_libs KF${KF_MAJOR_VERSION}::DBusAddons) endif() set(HAVE_QDBUS ${Qt${QT_MAJOR_VERSION}DBus_FOUND}) find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.19.0") set(GPGMEPP_SUPPORTS_SET_CURVE 1) endif() +if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.19.1") + set(GPGMEPP_KEY_CANSIGN_IS_FIXED 1) +endif() if (QT_MAJOR_VERSION STREQUAL "6") find_package(QGpgmeQt6 ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) else() find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.0") set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1) set(QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1) set(QGPGME_SUPPORTS_WKDLOOKUP 1) set(QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1) set(QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1) set(QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1) set(QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1) set(QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.18.0") set(QGPGME_SUPPORTS_KEY_REVOCATION 1) set(QGPGME_SUPPORTS_KEY_REFRESH 1) set(QGPGME_SUPPORTS_SET_FILENAME 1) set(QGPGME_SUPPORTS_SET_PRIMARY_UID 1) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.19.0") set(QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB 1) set(QGPGME_SUPPORTS_ARCHIVE_JOBS 1) set(QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS 1) endif() if (QT_MAJOR_VERSION STREQUAL "6") find_package(Qt6Core5Compat) endif() # Kdepimlibs packages find_package(KPim${KF_MAJOR_VERSION}Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KPim${KF_MAJOR_VERSION}Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED) find_package(KPim${KF_MAJOR_VERSION}IdentityManagement ${KIDENTITYMANAGEMENT_VERSION} CONFIG) find_package(KPim${KF_MAJOR_VERSION}MailTransport ${KMAILTRANSPORT_VERSION} CONFIG) find_package(KPim${KF_MAJOR_VERSION}AkonadiMime ${AKONADI_MIME_VERSION} CONFIG) find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(LibAssuan ${LIBASSUAN_REQUIRED_VERSION} REQUIRED) set_package_properties(LibAssuan PROPERTIES TYPE REQUIRED PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server" ) find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED) set_package_properties(LibGpgError PROPERTIES TYPE REQUIRED ) set(kleopatra_release FALSE) if(NOT kleopatra_release) find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE rc ERROR_QUIET) if(rc EQUAL 0) execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 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 ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE) string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}") set(kleopatra_version "${kleopatra_version}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}") endif() endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h) 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} ) if (WIN32) # On Windows, we need to use stuff deprecated since Qt 5.11, e.g. from QDesktopWidget ecm_set_disabled_deprecation_versions(QT 5.10.0 KF 5.103.0) else () ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.103.0) endif () if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() if(MINGW) # we do not care about different signedness of passed pointer arguments add_compile_options($<$:-Wno-pointer-sign>) endif() add_definitions(-DQT_NO_EMIT) remove_definitions(-DQT_NO_FOREACH) # Disable the use of QStringBuilder for operator+ to prevent crashes when # returning the result of concatenating string temporaries in lambdas. We do # this for example in some std::transform expressions. # This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066 # Alternatively, one would always have to remember to force the lambdas to # return a QString instead of QStringBuilder, but that's just too easy to # forget and, unfortunately, the compiler doesn't issue a warning if one forgets # this. So, it's just too dangerous. # One can still use QStringBuilder explicitly with the operator% if necessary. remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS) remove_definitions(-DQT_USE_QSTRINGBUILDER) kde_enable_exceptions() option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF) if (USE_UNITY_CMAKE_SUPPORT) set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON) endif() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() ecm_qt_install_logging_categories( EXPORT KLEOPATRA FILE kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) ki18n_install(po) if(KF${KF_MAJOR_VERSION}DocTools_FOUND) kdoctools_install(po) add_subdirectory(doc) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) diff --git a/config-kleopatra.h.cmake b/config-kleopatra.h.cmake index b0e93a71e..e95a096fb 100644 --- a/config-kleopatra.h.cmake +++ b/config-kleopatra.h.cmake @@ -1,50 +1,53 @@ /* DBus available */ #cmakedefine01 HAVE_QDBUS /* Whether QGpgME supports changing the expiration date of the primary key and the subkeys simultaneously */ #cmakedefine01 QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY /* Whether QGpgME supports retrieving the default value of a config entry */ #cmakedefine01 QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE /* Whether QGpgME supports WKD lookup */ #cmakedefine01 QGPGME_SUPPORTS_WKDLOOKUP /* Whether QGpgME supports specifying an import filter when importing keys */ #cmakedefine01 QGPGME_SUPPORTS_IMPORT_WITH_FILTER /* Whether QGpgME supports setting key origin when importing keys */ #cmakedefine01 QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN /* Whether QGpgME supports the export of secret keys */ #cmakedefine01 QGPGME_SUPPORTS_SECRET_KEY_EXPORT /* Whether QGpgME supports the export of secret subkeys */ #cmakedefine01 QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT /* Whether QGpgME supports receiving keys by their key ids */ #cmakedefine01 QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID /* Whether QGpgME supports revoking own OpenPGP keys */ #cmakedefine01 QGPGME_SUPPORTS_KEY_REVOCATION /* Whether QGpgME supports refreshing keys */ #cmakedefine01 QGPGME_SUPPORTS_KEY_REFRESH /* Whether QGpgME supports setting the file name of encrypted data */ #cmakedefine01 QGPGME_SUPPORTS_SET_FILENAME /* Whether QGpgME supports setting the primary user id of a key */ #cmakedefine01 QGPGME_SUPPORTS_SET_PRIMARY_UID /* Whether GpgME++ supports setting the curve when generating ECC card keys */ #cmakedefine01 GPGMEPP_SUPPORTS_SET_CURVE /* Whether QGpgME supports deferred start of ImportJob */ #cmakedefine01 QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB /* Whether QGpgME supports the sign/encrypt/decrypt/verify archive jobs */ #cmakedefine01 QGPGME_SUPPORTS_ARCHIVE_JOBS /* Whether QGpgME::Job provides the new progress signals */ #cmakedefine01 QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS + +/* Whether Key::canSign should be used instead of deprecated Key::canReallySign */ +#cmakedefine01 GPGMEPP_KEY_CANSIGN_IS_FIXED diff --git a/src/crypto/certificateresolver.cpp b/src/crypto/certificateresolver.cpp index c180693bf..3dd41740c 100644 --- a/src/crypto/certificateresolver.cpp +++ b/src/crypto/certificateresolver.cpp @@ -1,266 +1,270 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/certificateresolver.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "certificateresolver.h" #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace GpgME; using namespace KMime::Types; using namespace KMime::HeaderParsing; std::vector< std::vector > CertificateResolver::resolveRecipients(const std::vector &recipients, Protocol proto) { std::vector< std::vector > result; std::transform(recipients.begin(), recipients.end(), std::back_inserter(result), [proto](const Mailbox &recipient) { return resolveRecipient(recipient, proto); }); return result; } std::vector CertificateResolver::resolveRecipient(const Mailbox &recipient, Protocol proto) { std::vector result = KeyCache::instance()->findByEMailAddress(recipient.address().constData()); auto end = std::remove_if(result.begin(), result.end(), [](const Key &key) { return key.canEncrypt(); }); if (proto != UnknownProtocol) end = std::remove_if(result.begin(), end, [proto](const Key &key) { return key.protocol() != proto; }); result.erase(end, result.end()); return result; } std::vector< std::vector > CertificateResolver::resolveSigners(const std::vector &signers, Protocol proto) { std::vector< std::vector > result; std::transform(signers.begin(), signers.end(), std::back_inserter(result), [proto](const Mailbox &signer) { return resolveSigner(signer, proto); }); return result; } std::vector CertificateResolver::resolveSigner(const Mailbox &signer, Protocol proto) { std::vector result = KeyCache::instance()->findByEMailAddress(signer.address().constData()); auto end = std::remove_if(result.begin(), result.end(), [](const Key &key) { return key.hasSecret(); }); end = std::remove_if(result.begin(), end, +#if GPGMEPP_KEY_CANSIGN_IS_FIXED + [](const Key &key) { return key.canSign(); }); +#else [](const Key &key) { return key.canReallySign(); }); +#endif if (proto != UnknownProtocol) end = std::remove_if(result.begin(), end, [proto](const Key &key) { return key.protocol() != proto; }); result.erase(end, result.end()); return result; } class KConfigBasedRecipientPreferences::Private { friend class ::Kleo::Crypto::KConfigBasedRecipientPreferences; KConfigBasedRecipientPreferences *const q; public: explicit Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq); ~Private(); private: void ensurePrefsParsed() const; void writePrefs(); private: KSharedConfigPtr m_config; mutable QHash pgpPrefs; mutable QHash cmsPrefs; mutable bool m_parsed; mutable bool m_dirty; }; KConfigBasedRecipientPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false) { Q_ASSERT(m_config); } KConfigBasedRecipientPreferences::Private::~Private() { writePrefs(); } void KConfigBasedRecipientPreferences::Private::writePrefs() { if (!m_dirty) { return; } const auto pgpPrefsKeys = pgpPrefs.keys(); const auto cmsPrefsKeys = cmsPrefs.keys(); const QSet keys = QSet(pgpPrefsKeys.begin(), pgpPrefsKeys.end()) + QSet(cmsPrefsKeys.begin(), cmsPrefsKeys.end()); int n = 0; for (const QByteArray &i : keys) { KConfigGroup group(m_config, QStringLiteral("EncryptionPreference_%1").arg(n++)); group.writeEntry("email", i); const QByteArray pgp = pgpPrefs.value(i); if (!pgp.isEmpty()) { group.writeEntry("pgpCertificate", pgp); } const QByteArray cms = cmsPrefs.value(i); if (!cms.isEmpty()) { group.writeEntry("cmsCertificate", cms); } } m_config->sync(); m_dirty = false; } void KConfigBasedRecipientPreferences::Private::ensurePrefsParsed() const { if (m_parsed) { return; } const QStringList groups = m_config->groupList().filter(QRegularExpression(QStringLiteral("^EncryptionPreference_\\d+$"))); for (const QString &i : groups) { const KConfigGroup group(m_config, i); const QByteArray id = group.readEntry("email", QByteArray()); if (id.isEmpty()) { continue; } pgpPrefs.insert(id, group.readEntry("pgpCertificate", QByteArray())); cmsPrefs.insert(id, group.readEntry("cmsCertificate", QByteArray())); } m_parsed = true; } KConfigBasedRecipientPreferences::KConfigBasedRecipientPreferences(const KSharedConfigPtr &config) : d(new Private(config, this)) { } KConfigBasedRecipientPreferences::~KConfigBasedRecipientPreferences() { d->writePrefs(); } Key KConfigBasedRecipientPreferences::preferredCertificate(const Mailbox &recipient, Protocol protocol) { d->ensurePrefsParsed(); const QByteArray keyId = (protocol == CMS ? d->cmsPrefs : d->pgpPrefs).value(recipient.address()); return KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData()); } void KConfigBasedRecipientPreferences::setPreferredCertificate(const Mailbox &recipient, Protocol protocol, const Key &certificate) { d->ensurePrefsParsed(); if (!recipient.hasAddress()) { return; } (protocol == CMS ? d->cmsPrefs : d->pgpPrefs).insert(recipient.address(), certificate.keyID()); d->m_dirty = true; } class KConfigBasedSigningPreferences::Private { friend class ::Kleo::Crypto::KConfigBasedSigningPreferences; KConfigBasedSigningPreferences *const q; public: explicit Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq); ~Private(); private: void ensurePrefsParsed() const; void writePrefs(); private: KSharedConfigPtr m_config; mutable QByteArray pgpSigningCertificate; mutable QByteArray cmsSigningCertificate; mutable bool m_parsed; mutable bool m_dirty; }; KConfigBasedSigningPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq) : q(qq), m_config(config), m_parsed(false), m_dirty(false) { Q_ASSERT(m_config); } void KConfigBasedSigningPreferences::Private::ensurePrefsParsed() const { if (m_parsed) { return; } const KConfigGroup group(m_config, "SigningPreferences"); pgpSigningCertificate = group.readEntry("pgpSigningCertificate", QByteArray()); cmsSigningCertificate = group.readEntry("cmsSigningCertificate", QByteArray()); m_parsed = true; } void KConfigBasedSigningPreferences::Private::writePrefs() { if (!m_dirty) { return; } KConfigGroup group(m_config, "SigningPreferences"); group.writeEntry("pgpSigningCertificate", pgpSigningCertificate); group.writeEntry("cmsSigningCertificate", cmsSigningCertificate); m_config->sync(); m_dirty = false; } KConfigBasedSigningPreferences::Private::~Private() { writePrefs(); } KConfigBasedSigningPreferences::KConfigBasedSigningPreferences(const KSharedConfigPtr &config) : d(new Private(config, this)) { } KConfigBasedSigningPreferences::~KConfigBasedSigningPreferences() { d->writePrefs(); } Key KConfigBasedSigningPreferences::preferredCertificate(Protocol protocol) { d->ensurePrefsParsed(); const QByteArray keyId = (protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate); const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData()); return key.hasSecret() ? key : Key::null; } void KConfigBasedSigningPreferences::setPreferredCertificate(Protocol protocol, const Key &certificate) { d->ensurePrefsParsed(); (protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate) = certificate.keyID(); d->m_dirty = true; } diff --git a/src/crypto/gui/signingcertificateselectionwidget.cpp b/src/crypto/gui/signingcertificateselectionwidget.cpp index 130004ec1..25066f0c4 100644 --- a/src/crypto/gui/signingcertificateselectionwidget.cpp +++ b/src/crypto/gui/signingcertificateselectionwidget.cpp @@ -1,144 +1,148 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signingcertificateselectionwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007, 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signingcertificateselectionwidget.h" #include "ui_signingcertificateselectionwidget.h" #include "utils/keys.h" #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto::Gui; class SigningCertificateSelectionWidget::Private { friend class ::SigningCertificateSelectionWidget; SigningCertificateSelectionWidget *const q; public: explicit Private(SigningCertificateSelectionWidget *qq); ~Private(); static std::vector candidates(GpgME::Protocol prot); static void addCandidates(GpgME::Protocol prot, QComboBox *combo); private: Ui::SigningCertificateSelectionWidget ui; }; static GpgME::Key current_cert(const QComboBox &cb) { const QByteArray fpr = cb.itemData(cb.currentIndex()).toByteArray(); return KeyCache::instance()->findByFingerprint(fpr.constData()); } static void select_cert(QComboBox &cb, const GpgME::Key &key) { const QByteArray fpr = key.primaryFingerprint(); if (!fpr.isEmpty()) { cb.setCurrentIndex(cb.findData(fpr)); } } static void add_cert(QComboBox &cb, const GpgME::Key &key) { cb.addItem(Formatting::formatForComboBox(key), QVariant(QByteArray(key.primaryFingerprint()))); } SigningCertificateSelectionWidget::Private::Private(SigningCertificateSelectionWidget *qq) : q(qq), ui() { ui.setupUi(q); addCandidates(GpgME::CMS, ui.cmsCombo); addCandidates(GpgME::OpenPGP, ui.pgpCombo); ui.rememberCO->setChecked(true); } SigningCertificateSelectionWidget::Private::~Private() {} SigningCertificateSelectionWidget::SigningCertificateSelectionWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(this)) { } SigningCertificateSelectionWidget::~SigningCertificateSelectionWidget() {} void SigningCertificateSelectionWidget::setSelectedCertificates(const CertificatePair &certificates) { setSelectedCertificates(certificates.openpgp, certificates.cms); } void SigningCertificateSelectionWidget::setSelectedCertificates(const GpgME::Key &pgp, const GpgME::Key &cms) { select_cert(*d->ui.pgpCombo, pgp); select_cert(*d->ui.cmsCombo, cms); } std::vector SigningCertificateSelectionWidget::Private::candidates(GpgME::Protocol prot) { Q_ASSERT(prot != GpgME::UnknownProtocol); std::vector keys = KeyCache::instance()->keys(); auto end = keys.end(); end = std::remove_if(keys.begin(), end, [prot](const GpgME::Key &key) { return key.protocol() != prot; }); end = std::remove_if(keys.begin(), end, [](const GpgME::Key &key) { return !key.hasSecret(); }); Q_ASSERT(std::all_of(keys.begin(), end, [](const GpgME::Key &key) { return key.hasSecret(); })); +#if GPGMEPP_KEY_CANSIGN_IS_FIXED + end = std::remove_if(keys.begin(), end, [](const GpgME::Key &key) { return !key.canSign(); }); +#else end = std::remove_if(keys.begin(), end, [](const GpgME::Key &key) { return !key.canReallySign(); }); +#endif end = std::remove_if(keys.begin(), end, [](const GpgME::Key &key) { return key.isExpired(); }); end = std::remove_if(keys.begin(), end, [](const GpgME::Key &key) { return key.isRevoked(); }); keys.erase(end, keys.end()); return keys; } void SigningCertificateSelectionWidget::Private::addCandidates(GpgME::Protocol prot, QComboBox *combo) { const std::vector keys = candidates(prot); for (const GpgME::Key &i : keys) { add_cert(*combo, i); } } CertificatePair SigningCertificateSelectionWidget::selectedCertificates() const { return { current_cert(*d->ui.pgpCombo), current_cert(*d->ui.cmsCombo), }; } bool SigningCertificateSelectionWidget::rememberAsDefault() const { return d->ui.rememberCO->isChecked(); } void SigningCertificateSelectionWidget::setAllowedProtocols(const std::set &allowedProtocols) { setAllowedProtocols(allowedProtocols.find(GpgME::OpenPGP) != allowedProtocols.end(), allowedProtocols.find(GpgME::CMS) != allowedProtocols.end()); } void SigningCertificateSelectionWidget::setAllowedProtocols(bool pgp, bool cms) { d->ui.pgpLabel->setVisible(pgp); d->ui.pgpCombo->setVisible(pgp); d->ui.cmsLabel->setVisible(cms); d->ui.cmsCombo->setVisible(cms); } diff --git a/src/dialogs/certificateselectiondialog.cpp b/src/dialogs/certificateselectiondialog.cpp index 74017b9f8..73b7b38c7 100644 --- a/src/dialogs/certificateselectiondialog.cpp +++ b/src/dialogs/certificateselectiondialog.cpp @@ -1,485 +1,489 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/certificateselectiondialog.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 "certificateselectiondialog.h" #include "settings.h" #include "conf/groupsconfigdialog.h" #include #include #include #include #include "utils/tags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace Kleo::Commands; using namespace GpgME; CertificateSelectionDialog::Option CertificateSelectionDialog::optionsFromProtocol(Protocol proto) { switch (proto) { case OpenPGP: return CertificateSelectionDialog::OpenPGPFormat; case CMS: return CertificateSelectionDialog::CMSFormat; default: return CertificateSelectionDialog::AnyFormat; } } namespace { auto protocolFromOptions(CertificateSelectionDialog::Options options) { switch (options & CertificateSelectionDialog::AnyFormat) { case CertificateSelectionDialog::OpenPGPFormat: return GpgME::OpenPGP; case CertificateSelectionDialog::CMSFormat: return GpgME::CMS; default: return GpgME::UnknownProtocol; } } } class CertificateSelectionDialog::Private { friend class ::Kleo::Dialogs::CertificateSelectionDialog; CertificateSelectionDialog *const q; public: explicit Private(CertificateSelectionDialog *qq); private: void reload() { Command *const cmd = new ReloadKeysCommand(nullptr); cmd->setParentWidget(q); cmd->start(); } void create() { auto cmd = new NewOpenPGPCertificateCommand; cmd->setParentWidget(q); cmd->start(); } void lookup() { const auto cmd = new LookupCertificatesCommand(nullptr); cmd->setParentWidget(q); cmd->setProtocol(protocolFromOptions(options)); cmd->start(); } void manageGroups() { KConfigDialog *dialog = KConfigDialog::exists(GroupsConfigDialog::dialogName()); if (dialog) { // reparent the dialog to ensure it's shown on top of the modal CertificateSelectionDialog dialog->setParent(q, Qt::Dialog); } else { dialog = new GroupsConfigDialog(q); } dialog->show(); } void slotKeysMayHaveChanged(); void slotCurrentViewChanged(QAbstractItemView *newView); void slotSelectionChanged(); void slotDoubleClicked(const QModelIndex &idx); private: bool acceptable(const std::vector &keys, const std::vector &groups) { return !keys.empty() || !groups.empty(); } void updateLabelText() { ui.label.setText(!customLabelText.isEmpty() ? customLabelText : (options & MultiSelection) ? i18n("Please select one or more of the following certificates:") : i18n("Please select one of the following certificates:")); } private: std::vector connectedViews; QString customLabelText; Options options = AnyCertificate | AnyFormat; struct UI { QLabel label; SearchBar searchBar; TabWidget tabWidget; QDialogButtonBox buttonBox; QPushButton *createButton = nullptr; } ui; void setUpUI(CertificateSelectionDialog *q) { KDAB_SET_OBJECT_NAME(ui.label); KDAB_SET_OBJECT_NAME(ui.searchBar); KDAB_SET_OBJECT_NAME(ui.tabWidget); KDAB_SET_OBJECT_NAME(ui.buttonBox); auto vlay = new QVBoxLayout(q); vlay->addWidget(&ui.label); vlay->addWidget(&ui.searchBar); vlay->addWidget(&ui.tabWidget, 1); vlay->addWidget(&ui.buttonBox); QPushButton *const okButton = ui.buttonBox.addButton(QDialogButtonBox::Ok); okButton->setEnabled(false); ui.buttonBox.addButton(QDialogButtonBox::Close); QPushButton *const reloadButton = ui.buttonBox.addButton(i18n("Reload"), QDialogButtonBox::ActionRole); reloadButton->setToolTip(i18nc("@info:tooltip", "Refresh certificate list")); QPushButton *const importButton = ui.buttonBox.addButton(i18n("Import..."), QDialogButtonBox::ActionRole); importButton->setToolTip(i18nc("@info:tooltip", "Import certificate from file")); importButton->setAccessibleName(i18n("Import certificate")); QPushButton *const lookupButton = ui.buttonBox.addButton(i18n("Lookup..."), QDialogButtonBox::ActionRole); lookupButton->setToolTip(i18nc("@info:tooltip", "Look up certificate on server")); lookupButton->setAccessibleName(i18n("Look up certificate")); ui.createButton = ui.buttonBox.addButton(i18n("New..."), QDialogButtonBox::ActionRole); ui.createButton->setToolTip(i18nc("@info:tooltip", "Create a new OpenPGP certificate")); ui.createButton->setAccessibleName(i18n("Create certificate")); QPushButton *const groupsButton = ui.buttonBox.addButton(i18n("Groups..."), QDialogButtonBox::ActionRole); groupsButton->setToolTip(i18nc("@info:tooltip", "Manage certificate groups")); groupsButton->setAccessibleName(i18n("Manage groups")); groupsButton->setVisible(Settings().groupsEnabled()); connect(&ui.buttonBox, &QDialogButtonBox::accepted, q, &CertificateSelectionDialog::accept); connect(&ui.buttonBox, &QDialogButtonBox::rejected, q, &CertificateSelectionDialog::reject); connect(reloadButton, &QPushButton::clicked, q, [this] () { reload(); }); connect(lookupButton, &QPushButton::clicked, q, [this] () { lookup(); }); connect(ui.createButton, &QPushButton::clicked, q, [this] () { create(); }); connect(groupsButton, &QPushButton::clicked, q, [this] () { manageGroups(); }); connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, q, [this] () { slotKeysMayHaveChanged(); }); connect(importButton, &QPushButton::clicked, q, [importButton, q] () { importButton->setEnabled(false); auto cmd = new Kleo::ImportCertificateFromFileCommand(); connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [importButton]() { importButton->setEnabled(true); }); cmd->setParentWidget(q); cmd->start(); }); } }; CertificateSelectionDialog::Private::Private(CertificateSelectionDialog *qq) : q(qq) { setUpUI(q); ui.tabWidget.setFlatModel(AbstractKeyListModel::createFlatKeyListModel(q)); ui.tabWidget.setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel(q)); const auto tagKeys = Tags::tagKeys(); ui.tabWidget.flatModel()->setRemarkKeys(tagKeys); ui.tabWidget.hierarchicalModel()->setRemarkKeys(tagKeys); ui.tabWidget.connectSearchBar(&ui.searchBar); connect(&ui.tabWidget, &TabWidget::currentViewChanged, q, [this] (QAbstractItemView *view) { slotCurrentViewChanged(view); }); updateLabelText(); q->setWindowTitle(i18nc("@title:window", "Certificate Selection")); } CertificateSelectionDialog::CertificateSelectionDialog(QWidget *parent) : QDialog(parent), d(new Private(this)) { const KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.loadViews(config.data()); const KConfigGroup geometry(config, "Geometry"); resize(geometry.readEntry("size", size())); d->slotKeysMayHaveChanged(); } CertificateSelectionDialog::~CertificateSelectionDialog() {} void CertificateSelectionDialog::setCustomLabelText(const QString &txt) { if (txt == d->customLabelText) { return; } d->customLabelText = txt; d->updateLabelText(); } QString CertificateSelectionDialog::customLabelText() const { return d->customLabelText; } void CertificateSelectionDialog::setOptions(Options options) { Q_ASSERT((options & CertificateSelectionDialog::AnyCertificate) != 0); Q_ASSERT((options & CertificateSelectionDialog::AnyFormat) != 0); if (d->options == options) { return; } d->options = options; d->ui.tabWidget.setMultiSelection(options & MultiSelection); d->slotKeysMayHaveChanged(); d->updateLabelText(); d->ui.createButton->setVisible(options & OpenPGPFormat); } CertificateSelectionDialog::Options CertificateSelectionDialog::options() const { return d->options; } void CertificateSelectionDialog::setStringFilter(const QString &filter) { d->ui.tabWidget.setStringFilter(filter); } void CertificateSelectionDialog::setKeyFilter(const std::shared_ptr &filter) { d->ui.tabWidget.setKeyFilter(filter); } namespace { void selectRows(const QAbstractItemView *view, const QModelIndexList &indexes) { if (!view) { return; } QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); for (const QModelIndex &idx : std::as_const(indexes)) { if (idx.isValid()) { sm->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } } QModelIndexList getGroupIndexes(const KeyListModelInterface *model, const std::vector &groups) { QModelIndexList indexes; indexes.reserve(groups.size()); std::transform(groups.begin(), groups.end(), std::back_inserter(indexes), [model] (const KeyGroup &group) { return model->index(group); }); indexes.erase(std::remove_if(indexes.begin(), indexes.end(), [] (const QModelIndex &index) { return !index.isValid(); }), indexes.end()); return indexes; } } void CertificateSelectionDialog::selectCertificates(const std::vector &keys) { const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); selectRows(d->ui.tabWidget.currentView(), model->indexes(keys)); } void CertificateSelectionDialog::selectCertificate(const Key &key) { selectCertificates(std::vector(1, key)); } void CertificateSelectionDialog::selectGroups(const std::vector &groups) { const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); selectRows(d->ui.tabWidget.currentView(), getGroupIndexes(model, groups)); } namespace { QModelIndexList getSelectedRows(const QAbstractItemView *view) { if (!view) { return {}; } const QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); return sm->selectedRows(); } std::vector getGroups(const KeyListModelInterface *model, const QModelIndexList &indexes) { std::vector groups; groups.reserve(indexes.size()); std::transform(indexes.begin(), indexes.end(), std::back_inserter(groups), [model](const QModelIndex &idx) { return model->group(idx); }); groups.erase(std::remove_if(groups.begin(), groups.end(), std::mem_fn(&Kleo::KeyGroup::isNull)), groups.end()); return groups; } } std::vector CertificateSelectionDialog::selectedCertificates() const { const KeyListModelInterface *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); return model->keys(getSelectedRows(d->ui.tabWidget.currentView())); } Key CertificateSelectionDialog::selectedCertificate() const { const std::vector keys = selectedCertificates(); return keys.empty() ? Key() : keys.front(); } std::vector CertificateSelectionDialog::selectedGroups() const { const KeyListModelInterface *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); return getGroups(model, getSelectedRows(d->ui.tabWidget.currentView())); } void CertificateSelectionDialog::hideEvent(QHideEvent *e) { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.saveViews(config.data()); KConfigGroup geometry(config, "Geometry"); geometry.writeEntry("size", size()); QDialog::hideEvent(e); } void CertificateSelectionDialog::Private::slotKeysMayHaveChanged() { q->setEnabled(true); std::vector keys = (options & SecretKeys) ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys(); q->filterAllowedKeys(keys, options); const std::vector groups = (options & IncludeGroups) ? KeyCache::instance()->groups() : std::vector(); const std::vector selectedKeys = q->selectedCertificates(); const std::vector selectedGroups = q->selectedGroups(); if (AbstractKeyListModel *const model = ui.tabWidget.flatModel()) { model->setKeys(keys); model->setGroups(groups); } if (AbstractKeyListModel *const model = ui.tabWidget.hierarchicalModel()) { model->setKeys(keys); model->setGroups(groups); } q->selectCertificates(selectedKeys); q->selectGroups(selectedGroups); } void CertificateSelectionDialog::filterAllowedKeys(std::vector &keys, int options) { auto end = keys.end(); switch (options & AnyFormat) { case OpenPGPFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != OpenPGP; }); break; case CMSFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != CMS; }); break; default: case AnyFormat: ; } switch (options & AnyCertificate) { case SignOnly: +#if GPGMEPP_KEY_CANSIGN_IS_FIXED + end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canSign(); }); +#else end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canReallySign(); }); +#endif break; case EncryptOnly: end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canEncrypt(); }); break; default: case AnyCertificate: ; } if (options & SecretKeys) { end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.hasSecret(); }); } keys.erase(end, keys.end()); } void CertificateSelectionDialog::Private::slotCurrentViewChanged(QAbstractItemView *newView) { if (!Kleo::contains(connectedViews, newView)) { connectedViews.push_back(newView); connect(newView, &QAbstractItemView::doubleClicked, q, [this] (const QModelIndex &index) { slotDoubleClicked(index); }); Q_ASSERT(newView->selectionModel()); connect(newView->selectionModel(), &QItemSelectionModel::selectionChanged, q, [this] (const QItemSelection &, const QItemSelection &) { slotSelectionChanged(); }); } slotSelectionChanged(); } void CertificateSelectionDialog::Private::slotSelectionChanged() { if (QPushButton *const pb = ui.buttonBox.button(QDialogButtonBox::Ok)) { pb->setEnabled(acceptable(q->selectedCertificates(), q->selectedGroups())); } } void CertificateSelectionDialog::Private::slotDoubleClicked(const QModelIndex &idx) { QAbstractItemView *const view = ui.tabWidget.currentView(); Q_ASSERT(view); const auto *const model = ui.tabWidget.currentModel(); Q_ASSERT(model); Q_UNUSED(model) QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); sm->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); QMetaObject::invokeMethod(q, [this]() {q->accept();}, Qt::QueuedConnection); } void CertificateSelectionDialog::accept() { if (d->acceptable(selectedCertificates(), selectedGroups())) { QDialog::accept(); } } #include "moc_certificateselectiondialog.cpp" diff --git a/src/utils/clipboardmenu.cpp b/src/utils/clipboardmenu.cpp index 734221fd8..05d2b92ac 100644 --- a/src/utils/clipboardmenu.cpp +++ b/src/utils/clipboardmenu.cpp @@ -1,144 +1,150 @@ /* SPDX-FileCopyrightText: 2014-2021 Laurent Montel SPDX-License-Identifier: GPL-2.0-only */ +#include + #include "clipboardmenu.h" #include "kdtoolsglobal.h" #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; ClipboardMenu::ClipboardMenu(QObject *parent) : QObject{parent} { mClipboardMenu = new KActionMenu(i18n("Clipboard"), this); mImportClipboardAction = new QAction(i18n("Certificate Import"), this); mEncryptClipboardAction = new QAction(i18n("Encrypt..."), this); const Kleo::Settings settings{}; if (settings.cmsEnabled() && settings.cmsSigningAllowed()) { mSmimeSignClipboardAction = new QAction(i18n("S/MIME-Sign..."), this); } mOpenPGPSignClipboardAction = new QAction(i18n("OpenPGP-Sign..."), this); mDecryptVerifyClipboardAction = new QAction(i18n("Decrypt/Verify..."), this); KDAB_SET_OBJECT_NAME(mClipboardMenu); KDAB_SET_OBJECT_NAME(mImportClipboardAction); KDAB_SET_OBJECT_NAME(mEncryptClipboardAction); KDAB_SET_OBJECT_NAME(mSmimeSignClipboardAction); KDAB_SET_OBJECT_NAME(mOpenPGPSignClipboardAction); KDAB_SET_OBJECT_NAME(mDecryptVerifyClipboardAction); connect(mImportClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotImportClipboard); connect(mEncryptClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotEncryptClipboard); if (mSmimeSignClipboardAction) { connect(mSmimeSignClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotSMIMESignClipboard); } connect(mOpenPGPSignClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotOpenPGPSignClipboard); connect(mDecryptVerifyClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotDecryptVerifyClipboard); mClipboardMenu->addAction(mImportClipboardAction); mClipboardMenu->addAction(mEncryptClipboardAction); if (mSmimeSignClipboardAction) { mClipboardMenu->addAction(mSmimeSignClipboardAction); } mClipboardMenu->addAction(mOpenPGPSignClipboardAction); mClipboardMenu->addAction(mDecryptVerifyClipboardAction); connect(QApplication::clipboard(), &QClipboard::changed, this, &ClipboardMenu::slotEnableDisableActions); connect(KeyCache::instance().get(), &KeyCache::keyListingDone, this, &ClipboardMenu::slotEnableDisableActions); slotEnableDisableActions(); } ClipboardMenu::~ClipboardMenu() = default; void ClipboardMenu::setMainWindow(MainWindow *window) { mWindow = window; } KActionMenu *ClipboardMenu::clipboardMenu() const { return mClipboardMenu; } void ClipboardMenu::startCommand(Command *cmd) { Q_ASSERT(cmd); cmd->setParent(mWindow); cmd->start(); } void ClipboardMenu::slotImportClipboard() { startCommand(new ImportCertificateFromClipboardCommand(nullptr)); } void ClipboardMenu::slotEncryptClipboard() { startCommand(new EncryptClipboardCommand(nullptr)); } void ClipboardMenu::slotOpenPGPSignClipboard() { startCommand(new SignClipboardCommand(GpgME::OpenPGP, nullptr)); } void ClipboardMenu::slotSMIMESignClipboard() { startCommand(new SignClipboardCommand(GpgME::CMS, nullptr)); } void ClipboardMenu::slotDecryptVerifyClipboard() { startCommand(new DecryptVerifyClipboardCommand(nullptr)); } namespace { bool hasSigningKeys(GpgME::Protocol protocol) { if (!KeyCache::instance()->initialized()) { return false; } return Kleo::any_of(KeyCache::instance()->keys(), [protocol](const auto &k) { +#if GPGMEPP_KEY_CANSIGN_IS_FIXED + return k.hasSecret() && k.canSign() && (k.protocol() == protocol); +#else return k.hasSecret() && k.canReallySign() && (k.protocol() == protocol); +#endif }); } } void ClipboardMenu::slotEnableDisableActions() { const QSignalBlocker blocker(QApplication::clipboard()); mImportClipboardAction->setEnabled(ImportCertificateFromClipboardCommand::canImportCurrentClipboard()); mEncryptClipboardAction->setEnabled(EncryptClipboardCommand::canEncryptCurrentClipboard()); mOpenPGPSignClipboardAction->setEnabled(SignClipboardCommand::canSignCurrentClipboard() && hasSigningKeys(GpgME::OpenPGP)); if (mSmimeSignClipboardAction) { mSmimeSignClipboardAction->setEnabled(SignClipboardCommand::canSignCurrentClipboard() && hasSigningKeys(GpgME::CMS)); } mDecryptVerifyClipboardAction->setEnabled(DecryptVerifyClipboardCommand::canDecryptVerifyCurrentClipboard()); }