diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f11662766..e041896e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,646 +1,646 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(icons) add_subdirectory(mimetypes) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS pics/gpg4win.qrc selftest/registrycheck.cpp selftest/registrycheck.h utils/gnupg-registry.c utils/userinfo_win.cpp utils/winapi-helpers.cpp utils/winapi-helpers.h utils/windowsprocessdevice.cpp utils/windowsprocessdevice.h versioninfo.rc kleopatra.w32-manifest ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS ${_kleopatra_extra_uiserver_SRCS} selftest/uiservercheck.cpp selftest/uiservercheck.h uiserver/assuanserverconnection.cpp uiserver/assuanserverconnection.h uiserver/createchecksumscommand.cpp uiserver/createchecksumscommand.h uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandemailbase.h uiserver/decryptverifycommandfilesbase.cpp uiserver/decryptverifycommandfilesbase.h uiserver/echocommand.cpp uiserver/echocommand.h uiserver/encryptcommand.cpp uiserver/encryptcommand.h uiserver/importfilescommand.cpp uiserver/importfilescommand.h uiserver/prepencryptcommand.cpp uiserver/prepencryptcommand.h uiserver/prepsigncommand.cpp uiserver/prepsigncommand.h uiserver/selectcertificatecommand.cpp uiserver/sessiondata.cpp uiserver/sessiondata.h uiserver/signcommand.cpp uiserver/signcommand.h uiserver/signencryptfilescommand.cpp uiserver/uiserver.cpp uiserver/verifychecksumscommand.cpp uiserver/verifychecksumscommand.h ) set(_kleopatra_uiserver_extra_libs LibAssuan::LibAssuan LibGpgError::LibGpgError) if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() if(KPim6IdentityManagementCore_FOUND AND KPim6MailTransport_FOUND AND KPim6AkonadiMime_FOUND) set(_kleopatra_mail_libs KPim6::IdentityManagementCore # Export OpenPGP keys using WKS KPim6::MailTransport KPim6::AkonadiMime ) add_definitions(-DMAILAKONADI_ENABLED) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS ${_kleopatra_extra_SRCS} accessibility/accessiblelink.cpp accessibility/accessiblelink_p.h accessibility/accessiblerichtextlabel.cpp accessibility/accessiblerichtextlabel_p.h accessibility/accessiblevaluelabel.cpp accessibility/accessiblevaluelabel_p.h accessibility/accessiblewidgetfactory.cpp accessibility/accessiblewidgetfactory.h commands/addadskcommand.cpp commands/addadskcommand.h commands/addsubkeycommand.cpp commands/addsubkeycommand.h commands/adduseridcommand.cpp commands/adduseridcommand.h commands/authenticatepivcardapplicationcommand.cpp commands/authenticatepivcardapplicationcommand.h commands/cardcommand.cpp commands/cardcommand.h commands/certificatetocardcommand.cpp commands/certificatetocardcommand.h commands/certificatetopivcardcommand.cpp commands/certificatetopivcardcommand.h commands/certifycertificatecommand.cpp commands/certifycertificatecommand.h commands/certifygroupcommand.cpp commands/certifygroupcommand.h commands/changeexpirycommand.cpp commands/changeexpirycommand.h commands/changeownertrustcommand.cpp commands/changeownertrustcommand.h commands/changepassphrasecommand.cpp commands/changepassphrasecommand.h commands/changepincommand.cpp commands/changepincommand.h commands/changeroottrustcommand.cpp commands/changeroottrustcommand.h commands/checksumcreatefilescommand.cpp commands/checksumcreatefilescommand.h commands/checksumverifyfilescommand.cpp commands/checksumverifyfilescommand.h commands/clearcrlcachecommand.cpp commands/clearcrlcachecommand.h commands/command.cpp commands/command.h commands/createcsrforcardkeycommand.cpp commands/createcsrforcardkeycommand.h commands/creategroupcommand.cpp commands/creategroupcommand.h commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createopenpgpkeyfromcardkeyscommand.h commands/decryptverifyclipboardcommand.cpp commands/decryptverifyclipboardcommand.h commands/decryptverifyfilescommand.cpp commands/decryptverifyfilescommand.h commands/deletecertificatescommand.cpp commands/deletecertificatescommand.h commands/detailscommand.cpp commands/detailscommand.h commands/dumpcertificatecommand.cpp commands/dumpcertificatecommand.h commands/dumpcrlcachecommand.cpp commands/dumpcrlcachecommand.h - commands/encryptclipboardcommand.cpp - commands/encryptclipboardcommand.h commands/exportcertificatecommand.cpp commands/exportcertificatecommand.h commands/exportgroupscommand.cpp commands/exportgroupscommand.h commands/exportopenpgpcertstoservercommand.cpp commands/exportopenpgpcertstoservercommand.h commands/exportopenpgpcerttoprovidercommand.cpp commands/exportopenpgpcerttoprovidercommand.h commands/exportpaperkeycommand.cpp commands/exportpaperkeycommand.h commands/exportsecretkeycommand.cpp commands/exportsecretkeycommand.h commands/exportsecretsubkeycommand.cpp commands/exportsecretsubkeycommand.h commands/generateopenpgpcardkeysandcertificatecommand.cpp commands/generateopenpgpcardkeysandcertificatecommand.h commands/genrevokecommand.cpp commands/genrevokecommand.h commands/gnupgprocesscommand.cpp commands/gnupgprocesscommand.h commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromclipboardcommand.h commands/importcertificatefromdatacommand.cpp commands/importcertificatefromdatacommand.h commands/importcertificatefromfilecommand.cpp commands/importcertificatefromfilecommand.h commands/importcertificatefromkeyservercommand.cpp commands/importcertificatefromkeyservercommand.h commands/importcertificatefrompivcardcommand.cpp commands/importcertificatefrompivcardcommand.h commands/importcertificatescommand.cpp commands/importcertificatescommand.h commands/importcrlcommand.cpp commands/importcrlcommand.h commands/importpaperkeycommand.cpp commands/importpaperkeycommand.h commands/keytocardcommand.cpp commands/keytocardcommand.h commands/lookupcertificatescommand.cpp commands/lookupcertificatescommand.h commands/newcertificatesigningrequestcommand.cpp commands/newcertificatesigningrequestcommand.h commands/newopenpgpcertificatecommand.cpp commands/newopenpgpcertificatecommand.h commands/openpgpgeneratecardkeycommand.cpp commands/openpgpgeneratecardkeycommand.h commands/pivgeneratecardkeycommand.cpp commands/pivgeneratecardkeycommand.h commands/refreshcertificatescommand.cpp commands/refreshcertificatescommand.h commands/refreshopenpgpcertscommand.cpp commands/refreshopenpgpcertscommand.h commands/refreshx509certscommand.cpp commands/refreshx509certscommand.h commands/reloadkeyscommand.cpp commands/reloadkeyscommand.h commands/revokecertificationcommand.cpp commands/revokecertificationcommand.h commands/revokekeycommand.cpp commands/revokekeycommand.h commands/revokeuseridcommand.cpp commands/revokeuseridcommand.h commands/selftestcommand.cpp commands/selftestcommand.h commands/setinitialpincommand.cpp commands/setinitialpincommand.h commands/setpivcardapplicationadministrationkeycommand.cpp commands/setpivcardapplicationadministrationkeycommand.h commands/setprimaryuseridcommand.cpp commands/setprimaryuseridcommand.h - commands/signclipboardcommand.cpp - commands/signclipboardcommand.h + commands/signencryptclipboardcommand.cpp + commands/signencryptclipboardcommand.h commands/signencryptfilescommand.cpp commands/signencryptfilescommand.h commands/signencryptfoldercommand.cpp commands/signencryptfoldercommand.h commands/togglecertificateenabledcommand.cpp commands/togglecertificateenabledcommand.h commands/viewemailfilescommand.cpp commands/viewemailfilescommand.h conf/configuredialog.cpp conf/configuredialog.h conf/groupsconfigdialog.cpp conf/groupsconfigdialog.h conf/groupsconfigwidget.cpp conf/groupsconfigwidget.h crypto/autodecryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.h crypto/certificateresolver.cpp crypto/certificateresolver.h crypto/checksumsutils_p.cpp crypto/checksumsutils_p.h crypto/controller.cpp crypto/controller.h crypto/createchecksumscontroller.cpp crypto/createchecksumscontroller.h crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyemailcontroller.h crypto/decryptverifyfilescontroller.cpp crypto/decryptverifyfilescontroller.h crypto/decryptverifytask.cpp crypto/decryptverifytask.h crypto/encryptemailcontroller.cpp crypto/encryptemailcontroller.h crypto/encryptemailtask.cpp crypto/encryptemailtask.h crypto/gui/certificatelineedit.cpp crypto/gui/certificatelineedit.h crypto/gui/certificateselectionline.cpp crypto/gui/certificateselectionline.h crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/decryptverifyfilesdialog.h crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfileswizard.h crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyoperationwidget.h crypto/gui/encryptemailwizard.cpp crypto/gui/encryptemailwizard.h crypto/gui/newresultpage.cpp crypto/gui/newresultpage.h crypto/gui/objectspage.cpp crypto/gui/objectspage.h crypto/gui/resolverecipientspage.cpp crypto/gui/resolverecipientspage.h crypto/gui/resultitemwidget.cpp crypto/gui/resultitemwidget.h crypto/gui/resultlistwidget.cpp crypto/gui/resultlistwidget.h crypto/gui/resultpage.cpp crypto/gui/resultpage.h crypto/gui/signemailwizard.cpp crypto/gui/signemailwizard.h crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/signencryptemailconflictdialog.h crypto/gui/signencryptfilesdialog.cpp crypto/gui/signencryptfilesdialog.h crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwidget.h crypto/gui/signencryptwizard.cpp crypto/gui/signencryptwizard.h crypto/gui/signerresolvepage.cpp crypto/gui/signerresolvepage.h crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/signingcertificateselectiondialog.h crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectionwidget.h crypto/gui/unknownrecipientwidget.cpp crypto/gui/unknownrecipientwidget.h crypto/gui/verifychecksumsdialog.cpp crypto/gui/verifychecksumsdialog.h crypto/gui/wizard.cpp crypto/gui/wizard.h crypto/gui/wizardpage.cpp crypto/gui/wizardpage.h crypto/newsignencryptemailcontroller.cpp crypto/newsignencryptemailcontroller.h crypto/recipient.cpp crypto/recipient.h crypto/sender.cpp crypto/sender.h crypto/signemailcontroller.cpp crypto/signemailcontroller.h crypto/signemailtask.cpp crypto/signemailtask.h crypto/signencryptfilescontroller.cpp crypto/signencryptfilescontroller.h crypto/signencrypttask.cpp crypto/signencrypttask.h crypto/task.cpp crypto/task.h crypto/taskcollection.cpp crypto/taskcollection.h crypto/verifychecksumscontroller.cpp crypto/verifychecksumscontroller.h dialogs/addsubkeydialog.cpp dialogs/addsubkeydialog.h dialogs/adduseriddialog.cpp dialogs/adduseriddialog.h dialogs/animatedexpander.cpp dialogs/animatedexpander.h dialogs/certificatedetailsdialog.cpp dialogs/certificatedetailsdialog.h dialogs/certificatedetailsinputwidget.cpp dialogs/certificatedetailsinputwidget.h dialogs/certificatedetailswidget.cpp dialogs/certificatedetailswidget.h dialogs/certificatedumpwidget.cpp dialogs/certificatedumpwidget.h dialogs/certificateselectiondialog.cpp dialogs/certificateselectiondialog.h dialogs/certifycertificatedialog.cpp dialogs/certifycertificatedialog.h dialogs/certifywidget.cpp dialogs/certifywidget.h dialogs/createcsrforcardkeydialog.cpp dialogs/createcsrforcardkeydialog.h dialogs/copytosmartcarddialog.cpp dialogs/copytosmartcarddialog.h dialogs/debugdialog.cpp dialogs/debugdialog.h dialogs/deletecertificatesdialog.cpp dialogs/deletecertificatesdialog.h dialogs/editgroupdialog.cpp dialogs/editgroupdialog.h dialogs/expirydialog.cpp dialogs/expirydialog.h dialogs/exportdialog.cpp dialogs/exportdialog.h dialogs/gencardkeydialog.cpp dialogs/gencardkeydialog.h dialogs/groupdetailsdialog.cpp dialogs/groupdetailsdialog.h dialogs/lookupcertificatesdialog.cpp dialogs/lookupcertificatesdialog.h dialogs/pivcardapplicationadministrationkeyinputdialog.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.h dialogs/revokekeydialog.cpp dialogs/revokekeydialog.h dialogs/revokerswidget.cpp dialogs/revokerswidget.h dialogs/selftestdialog.cpp dialogs/selftestdialog.h dialogs/setinitialpindialog.cpp dialogs/setinitialpindialog.h + dialogs/signencryptclipboarddialog.cpp + dialogs/signencryptclipboarddialog.h dialogs/smartcardwindow.cpp dialogs/smartcardwindow.h dialogs/subkeyswidget.cpp dialogs/subkeyswidget.h dialogs/trustchainwidget.cpp dialogs/trustchainwidget.h dialogs/updatenotification.cpp dialogs/updatenotification.h dialogs/useridswidget.cpp dialogs/useridswidget.h dialogs/weboftrustwidget.cpp dialogs/weboftrustwidget.h interfaces/anchorprovider.h interfaces/focusfirstchild.h newcertificatewizard/advancedsettingsdialog.cpp newcertificatewizard/advancedsettingsdialog_p.h newcertificatewizard/enterdetailspage.cpp newcertificatewizard/enterdetailspage_p.h newcertificatewizard/keycreationpage.cpp newcertificatewizard/keycreationpage_p.h newcertificatewizard/listwidget.cpp newcertificatewizard/listwidget.h newcertificatewizard/newcertificatewizard.cpp newcertificatewizard/newcertificatewizard.h newcertificatewizard/resultpage.cpp newcertificatewizard/resultpage_p.h newcertificatewizard/wizardpage.cpp newcertificatewizard/wizardpage_p.h selftest/compliancecheck.cpp selftest/compliancecheck.h selftest/enginecheck.cpp selftest/enginecheck.h selftest/gpgagentcheck.cpp selftest/gpgagentcheck.h selftest/gpgconfcheck.cpp selftest/gpgconfcheck.h selftest/libkleopatrarccheck.cpp selftest/libkleopatrarccheck.h selftest/selftest.cpp selftest/selftest.h smartcard/algorithminfo.h smartcard/card.cpp smartcard/card.h smartcard/deviceinfowatcher.cpp smartcard/deviceinfowatcher.h smartcard/keypairinfo.cpp smartcard/keypairinfo.h smartcard/netkeycard.cpp smartcard/netkeycard.h smartcard/openpgpcard.cpp smartcard/openpgpcard.h smartcard/p15card.cpp smartcard/p15card.h smartcard/pivcard.cpp smartcard/pivcard.h smartcard/readerstatus.cpp smartcard/readerstatus.h smartcard/utils.cpp smartcard/utils.h utils/accessibility.cpp utils/accessibility.h utils/action_data.cpp utils/action_data.h utils/applicationstate.cpp utils/applicationstate.h utils/archivedefinition.cpp utils/archivedefinition.h utils/certificatepair.h utils/clipboardmenu.cpp utils/clipboardmenu.h utils/debug-helpers.cpp utils/debug-helpers.h utils/dragqueen.cpp utils/dragqueen.h utils/email.cpp utils/email.h utils/emptypassphraseprovider.cpp utils/emptypassphraseprovider.h utils/filedialog.cpp utils/filedialog.h utils/fileutils.cpp utils/fileutils.h utils/gui-helper.cpp utils/gui-helper.h utils/headerview.cpp utils/headerview.h utils/input.cpp utils/input.h utils/iodevicelogger.cpp utils/iodevicelogger.h utils/kdpipeiodevice.cpp utils/kdpipeiodevice.h utils/keyexportdraghandler.cpp utils/keyexportdraghandler.h utils/kuniqueservice.cpp utils/kuniqueservice.h utils/log.cpp utils/log.h utils/memory-helpers.h utils/migration.cpp utils/migration.h utils/output.cpp utils/output.h utils/overwritedialog.cpp utils/overwritedialog.h utils/path-helper.cpp utils/path-helper.h utils/scrollarea.cpp utils/scrollarea.h utils/statusmessage.cpp utils/statusmessage.h utils/systemtrayicon.cpp utils/systemtrayicon.h utils/tags.cpp utils/tags.h utils/types.cpp utils/types.h utils/userinfo.cpp utils/userinfo.h utils/writecertassuantransaction.cpp utils/writecertassuantransaction.h utils/wsastarter.cpp utils/wsastarter.h view/anchorcache.cpp view/anchorcache_p.h view/cardkeysview.cpp view/cardkeysview.h view/htmllabel.cpp view/htmllabel.h view/infofield.cpp view/infofield.h view/keycacheoverlay.cpp view/keycacheoverlay.h view/keylistcontroller.cpp view/keylistcontroller.h view/keytreeview.cpp view/keytreeview.h view/netkeywidget.cpp view/netkeywidget.h view/overlaywidget.cpp view/overlaywidget.h view/p15cardwidget.cpp view/p15cardwidget.h view/padwidget.cpp view/padwidget.h view/pgpcardwidget.cpp view/pgpcardwidget.h view/pivcardwidget.cpp view/pivcardwidget.h view/progressoverlay.cpp view/progressoverlay.h view/searchbar.cpp view/searchbar.h view/smartcardactions.cpp view/smartcardactions.h view/smartcardswidget.cpp view/smartcardswidget.h view/smartcardwidget.cpp view/smartcardwidget.h view/tabwidget.cpp view/tabwidget.h view/textoverlay.cpp view/textoverlay.h view/urllabel.cpp view/urllabel.h view/waitwidget.cpp view/waitwidget.h view/welcomewidget.cpp view/welcomewidget.h aboutdata.cpp aboutdata.h kleopatra.qrc kleopatraapplication.cpp kleopatraapplication.h main.cpp mainwindow.cpp mainwindow.h systrayicon.cpp systrayicon.h kleopatra_options.h ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) configure_file (kleopatra.w32-manifest.in kleopatra.w32-manifest) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/kleopatra.w32-manifest ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc conf/kmessageboxdontaskagainstorage.cpp conf/kmessageboxdontaskagainstorage.h ${_kleopatra_SRCS} ) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp conf/kleopageconfigdialog.h ${_kleopatra_SRCS}) ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/setinitialpindialog.ui newcertificatewizard/listwidget.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/settings.kcfgc kcfg/smimevalidationpreferences.kcfgc kcfg/tooltippreferences.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) # For the ConfigureDialog & KCMs target_link_libraries(kleopatra_bin kcm_kleopatra_static) #if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) # set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON) #endif() set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) if (WIN32) set(_kleopatra_platform_libs "secur32") endif () target_link_libraries(kleopatra_bin Gpgmepp KPim6::Libkleo KPim6::Mime KPim6::MimeTreeParserWidgets KF6::Codecs KF6::CoreAddons KF6::Crash KF6::I18n KF6::IconThemes KF6::ItemModels KF6::KIOCore KF6::KIOWidgets KF6::WindowSystem KF6::XmlGui Qt::Network Qt::PrintSupport # Printing secret keys kleopatraclientcore ${_kleopatra_extra_libs} ${_kleopatra_mail_libs} ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} ${_kleopatra_platform_libs} ) target_link_libraries(kleopatra_bin QGpgmeQt6) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES data/kleopatra-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR}) install( PROGRAMS data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus ) install(FILES kleopatradebugcommandsrc DESTINATION ${KDE_INSTALL_CONFDIR}) diff --git a/src/commands/signclipboardcommand.cpp b/src/commands/signclipboardcommand.cpp deleted file mode 100644 index b94e2ca11..000000000 --- a/src/commands/signclipboardcommand.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - commands/signclipboardcommand.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 <config-kleopatra.h> - -#include "signclipboardcommand.h" - -#ifndef QT_NO_CLIPBOARD - -#include "command_p.h" - -#include <crypto/signemailcontroller.h> - -#include <utils/input.h> -#include <utils/output.h> - -#include <Libkleo/Stl_Util> - -#include "kleopatra_debug.h" -#include <KLocalizedString> - -#include <QApplication> -#include <QClipboard> -#include <QMimeData> - -#include <exception> - -using namespace Kleo; -using namespace Kleo::Commands; -using namespace Kleo::Crypto; - -class SignClipboardCommand::Private : public Command::Private -{ - friend class ::Kleo::Commands::SignClipboardCommand; - SignClipboardCommand *q_func() const - { - return static_cast<SignClipboardCommand *>(q); - } - -public: - explicit Private(SignClipboardCommand *qq, KeyListController *c); - ~Private() override; - - void init(); - -private: - void slotSignersResolved(); - void slotControllerDone() - { - finished(); - } - void slotControllerError(int, const QString &) - { - finished(); - } - -private: - std::shared_ptr<const ExecutionContext> shared_qq; - std::shared_ptr<Input> input; - SignEMailController controller; -}; - -SignClipboardCommand::Private *SignClipboardCommand::d_func() -{ - return static_cast<Private *>(d.get()); -} -const SignClipboardCommand::Private *SignClipboardCommand::d_func() const -{ - return static_cast<const Private *>(d.get()); -} - -#define d d_func() -#define q q_func() - -SignClipboardCommand::Private::Private(SignClipboardCommand *qq, KeyListController *c) - : Command::Private(qq, c) - , shared_qq(qq, [](SignClipboardCommand *) {}) - , input() - , controller(SignEMailController::ClipboardMode) -{ -} - -SignClipboardCommand::Private::~Private() -{ - qCDebug(KLEOPATRA_LOG); -} - -SignClipboardCommand::SignClipboardCommand(GpgME::Protocol protocol, KeyListController *c) - : Command(new Private(this, c)) -{ - d->init(); - d->controller.setProtocol(protocol); -} - -SignClipboardCommand::SignClipboardCommand(GpgME::Protocol protocol, QAbstractItemView *v, KeyListController *c) - : Command(v, new Private(this, c)) -{ - d->init(); - d->controller.setProtocol(protocol); -} - -void SignClipboardCommand::Private::init() -{ - controller.setExecutionContext(shared_qq); - controller.setDetachedSignature(false); - connect(&controller, &Controller::done, q, [this]() { - slotControllerDone(); - }); - connect(&controller, &Controller::error, q, [this](int err, const QString &details) { - slotControllerError(err, details); - }); -} - -SignClipboardCommand::~SignClipboardCommand() -{ - qCDebug(KLEOPATRA_LOG); -} - -// static -bool SignClipboardCommand::canSignCurrentClipboard() -{ - bool canSign = false; - if (const QClipboard *const clip = QApplication::clipboard()) { - if (const QMimeData *const mime = clip->mimeData()) { - canSign = mime->hasText(); - } - } - return canSign; -} - -void SignClipboardCommand::doStart() -{ - try { - // snapshot clipboard content here, in case it's being changed... - d->input = Input::createFromClipboard(); - - connect(&d->controller, &SignEMailController::signersResolved, this, [this]() { - d->slotSignersResolved(); - }); - - d->controller.startResolveSigners(); - - } catch (const std::exception &e) { - d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error")); - d->finished(); - } -} - -void SignClipboardCommand::Private::slotSignersResolved() -{ - try { - controller.setInputAndOutput(input, Output::createFromClipboard()); - input.reset(); // no longer needed, so don't keep a reference - controller.start(); - } catch (const std::exception &e) { - information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error")); - finished(); - } -} - -void SignClipboardCommand::doCancel() -{ - qCDebug(KLEOPATRA_LOG); - d->controller.cancel(); -} - -#undef d -#undef q - -#include "moc_signclipboardcommand.cpp" - -#endif // QT_NO_CLIPBOARD diff --git a/src/commands/signencryptclipboardcommand.cpp b/src/commands/signencryptclipboardcommand.cpp new file mode 100644 index 000000000..4ea1bcd31 --- /dev/null +++ b/src/commands/signencryptclipboardcommand.cpp @@ -0,0 +1,123 @@ +/* -*- mode: c++; c-basic-offset:4 -*- + commands/signclipboardcommand.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 <config-kleopatra.h> + +#include "signencryptclipboardcommand.h" + +#ifndef QT_NO_CLIPBOARD + +#include "dialogs/signencryptclipboarddialog.h" + +#include "command_p.h" + +#include "kleopatra_debug.h" +#include <KLocalizedString> + +#include <exception> + +#include <QApplication> +#include <QClipboard> +#include <QMimeData> + +using namespace Kleo; +using namespace Kleo::Commands; + +class SignEncryptClipboardCommand::Private : public Command::Private +{ + friend class ::Kleo::Commands::SignEncryptClipboardCommand; + SignEncryptClipboardCommand *q_func() const + { + return static_cast<SignEncryptClipboardCommand *>(q); + } + +public: + explicit Private(SignEncryptClipboardCommand *qq, KeyListController *c); + ~Private() override; + + void init(); + void slotDialogRejected(); + +private: + std::unique_ptr<SignEncryptClipboardDialog> dialog; +}; + +SignEncryptClipboardCommand::Private *SignEncryptClipboardCommand::d_func() +{ + return static_cast<Private *>(d.get()); +} +const SignEncryptClipboardCommand::Private *SignEncryptClipboardCommand::d_func() const +{ + return static_cast<const Private *>(d.get()); +} + +#define d d_func() +#define q q_func() + +SignEncryptClipboardCommand::Private::Private(SignEncryptClipboardCommand *qq, KeyListController *c) + : Command::Private(qq, c) +{ +} + +SignEncryptClipboardCommand::Private::~Private() +{ + qCDebug(KLEOPATRA_LOG); +} + +void SignEncryptClipboardCommand::Private::slotDialogRejected() +{ + canceled(); +} + +SignEncryptClipboardCommand::SignEncryptClipboardCommand(KeyListController *c) + : Command(new Private(this, c)) +{ +} + +SignEncryptClipboardCommand::~SignEncryptClipboardCommand() +{ + qCDebug(KLEOPATRA_LOG); +} + +void SignEncryptClipboardCommand::doStart() +{ + try { + d->dialog = std::make_unique<SignEncryptClipboardDialog>(); + } catch (const std::exception &e) { + d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error")); + d->finished(); + } + d->dialog->show(); + connect(d->dialog.get(), &QDialog::rejected, this, [this]() { + d->slotDialogRejected(); + }); +} + +void SignEncryptClipboardCommand::doCancel() +{ + qCDebug(KLEOPATRA_LOG); + d->dialog->deleteLater(); +} + +bool SignEncryptClipboardCommand::canSignEncryptCurrentClipboard() +{ + if (const auto &clip = QApplication::clipboard()) { + if (const auto &mime = clip->mimeData()) { + return mime->hasText(); + } + } + return false; +} + +#undef d +#undef q + +#include "moc_signencryptclipboardcommand.cpp" + +#endif // QT_NO_CLIPBOARD diff --git a/src/commands/signclipboardcommand.h b/src/commands/signencryptclipboardcommand.h similarity index 60% rename from src/commands/signclipboardcommand.h rename to src/commands/signencryptclipboardcommand.h index d33035176..02837c4c1 100644 --- a/src/commands/signclipboardcommand.h +++ b/src/commands/signencryptclipboardcommand.h @@ -1,48 +1,43 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/signclipboardcommand.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 #include <commands/command.h> #ifndef QT_NO_CLIPBOARD -#include <utils/types.h> - -#include <gpgme++/global.h> - namespace Kleo { namespace Commands { -class SignClipboardCommand : public Command +class SignEncryptClipboardCommand : public Command { Q_OBJECT public: - explicit SignClipboardCommand(GpgME::Protocol protocol, QAbstractItemView *view, KeyListController *parent); - explicit SignClipboardCommand(GpgME::Protocol protocol, KeyListController *parent); - ~SignClipboardCommand() override; + explicit SignEncryptClipboardCommand(KeyListController *parent); + ~SignEncryptClipboardCommand() override; - static bool canSignCurrentClipboard(); + static bool canSignEncryptCurrentClipboard(); private: void doStart() override; void doCancel() override; private: class Private; inline Private *d_func(); inline const Private *d_func() const; }; } } #endif // QT_NO_CLIPBOARD diff --git a/src/crypto/gui/resultpage.cpp b/src/crypto/gui/resultpage.cpp index 9025916a8..6c8a5698e 100644 --- a/src/crypto/gui/resultpage.cpp +++ b/src/crypto/gui/resultpage.cpp @@ -1,179 +1,178 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultpage.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 <config-kleopatra.h> #include "resultitemwidget.h" #include "resultlistwidget.h" #include "resultpage.h" #include <crypto/taskcollection.h> #include <utils/scrollarea.h> #include <KLocalizedString> #include <QCheckBox> #include <QHash> #include <QLabel> #include <QProgressBar> #include <QVBoxLayout> using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class ResultPage::Private { ResultPage *const q; public: explicit Private(ResultPage *qq); void progress(int progress, int total); void result(const std::shared_ptr<const Task::Result> &result); void started(const std::shared_ptr<Task> &result); void allDone(); QLabel *labelForTag(const QString &tag); std::shared_ptr<TaskCollection> m_tasks; QProgressBar *m_progressBar; QHash<QString, QLabel *> m_progressLabelByTag; QVBoxLayout *m_progressLabelLayout; int m_lastErrorItemIndex = 0; ResultListWidget *m_resultList; QCheckBox *m_keepOpenCB; }; ResultPage::Private::Private(ResultPage *qq) : q(qq) { QBoxLayout *const layout = new QVBoxLayout(q); auto const labels = new QWidget; m_progressLabelLayout = new QVBoxLayout(labels); layout->addWidget(labels); m_progressBar = new QProgressBar; layout->addWidget(m_progressBar); m_resultList = new ResultListWidget; layout->addWidget(m_resultList); m_keepOpenCB = new QCheckBox; m_keepOpenCB->setText(i18n("Keep open after operation completed")); m_keepOpenCB->setChecked(true); layout->addWidget(m_keepOpenCB); } void ResultPage::Private::progress(int progress, int total) { Q_ASSERT(progress >= 0); Q_ASSERT(total >= 0); m_progressBar->setRange(0, total); m_progressBar->setValue(progress); } void ResultPage::Private::allDone() { Q_ASSERT(m_tasks); q->setAutoAdvance(!m_keepOpenCB->isChecked() && !m_tasks->errorOccurred()); - m_progressBar->setRange(0, 100); - m_progressBar->setValue(100); + m_progressBar->setVisible(false); m_tasks.reset(); const auto progressLabelByTagKeys{m_progressLabelByTag.keys()}; for (const QString &i : progressLabelByTagKeys) { if (!i.isEmpty()) { - m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i)); + m_progressLabelByTag.value(i)->setText({}); } else { - m_progressLabelByTag.value(i)->setText(i18n("All operations completed.")); + m_progressLabelByTag.value(i)->setText({}); } } Q_EMIT q->completeChanged(); } void ResultPage::Private::result(const std::shared_ptr<const Task::Result> &) { } void ResultPage::Private::started(const std::shared_ptr<Task> &task) { Q_ASSERT(task); const QString tag = task->tag(); QLabel *const label = labelForTag(tag); Q_ASSERT(label); if (tag.isEmpty()) { label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label())); } else { label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label())); } } ResultPage::ResultPage(QWidget *parent, Qt::WindowFlags flags) : WizardPage(parent, flags) , d(new Private(this)) { setTitle(i18n("<b>Results</b>")); } ResultPage::~ResultPage() { } bool ResultPage::keepOpenWhenDone() const { return d->m_keepOpenCB->isChecked(); } void ResultPage::setKeepOpenWhenDone(bool keep) { d->m_keepOpenCB->setChecked(keep); } void ResultPage::setTaskCollection(const std::shared_ptr<TaskCollection> &coll) { Q_ASSERT(!d->m_tasks); if (d->m_tasks == coll) { return; } d->m_tasks = coll; Q_ASSERT(d->m_tasks); d->m_resultList->setTaskCollection(coll); connect(d->m_tasks.get(), &TaskCollection::progress, this, [this](int current, int total) { d->progress(current, total); }); connect(d->m_tasks.get(), SIGNAL(done()), this, SLOT(allDone())); connect(d->m_tasks.get(), SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)), this, SLOT(result(std::shared_ptr<const Kleo::Crypto::Task::Result>))); connect(d->m_tasks.get(), SIGNAL(started(std::shared_ptr<Kleo::Crypto::Task>)), this, SLOT(started(std::shared_ptr<Kleo::Crypto::Task>))); for (const std::shared_ptr<Task> &i : d->m_tasks->tasks()) { // create labels for all tags in collection Q_ASSERT(i && d->labelForTag(i->tag())); Q_UNUSED(i) } Q_EMIT completeChanged(); } QLabel *ResultPage::Private::labelForTag(const QString &tag) { if (QLabel *const label = m_progressLabelByTag.value(tag)) { return label; } auto label = new QLabel; label->setTextFormat(Qt::RichText); label->setWordWrap(true); m_progressLabelLayout->addWidget(label); m_progressLabelByTag.insert(tag, label); return label; } bool ResultPage::isComplete() const { return d->m_tasks ? d->m_tasks->allTasksCompleted() : true; } #include "moc_resultpage.cpp" diff --git a/src/crypto/gui/signencryptfilesdialog.cpp b/src/crypto/gui/signencryptfilesdialog.cpp index b976e06ab..1126b15ef 100644 --- a/src/crypto/gui/signencryptfilesdialog.cpp +++ b/src/crypto/gui/signencryptfilesdialog.cpp @@ -1,688 +1,676 @@ /* crypto/gui/signencryptfileswizard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 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 <config-kleopatra.h> #include "kleopatra_debug.h" #include "signencryptfilesdialog.h" #include "signencryptwidget.h" #include "resultpage.h" #include "utils/scrollarea.h" #include <fileoperationspreferences.h> #include <settings.h> #include <KColorScheme> #include <KConfigGroup> #include <KLocalizedString> #include <KMessageBox> #include <KMessageWidget> #include <KSeparator> #include <KSharedConfig> #include <KTitleWidget> #include <Libkleo/Compliance> #include <Libkleo/FileNameRequester> #include <Libkleo/Formatting> #include <Libkleo/GnuPG> #include <Libkleo/SystemInfo> #include <QCheckBox> #include <QGroupBox> #include <QIcon> #include <QLabel> #include <QPushButton> #include <QStackedLayout> #include <QStyle> #include <QVBoxLayout> #include <QWindow> #include <gpgme++/key.h> #include <array> using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto::Gui; class FileNameRequesterWithIcon : public QWidget { Q_OBJECT public: explicit FileNameRequesterWithIcon(QDir::Filters filter, QWidget *parent = nullptr) : QWidget(parent) { auto layout = new QHBoxLayout{this}; layout->setContentsMargins(0, 0, 0, 0); mIconLabel = new QLabel{this}; mRequester = new FileNameRequester{filter, this}; mRequester->setExistingOnly(false); layout->addWidget(mIconLabel); layout->addWidget(mRequester); setFocusPolicy(mRequester->focusPolicy()); setFocusProxy(mRequester); connect(mRequester, &FileNameRequester::fileNameChanged, this, &FileNameRequesterWithIcon::fileNameChanged); } void setIcon(const QIcon &icon) { mIconLabel->setPixmap(icon.pixmap(32, 32)); } void setFileName(const QString &name) { mRequester->setFileName(name); } QString fileName() const { return mRequester->fileName(); } void setNameFilter(const QString &nameFilter) { mRequester->setNameFilter(nameFilter); } QString nameFilter() const { return mRequester->nameFilter(); } FileNameRequester *requester() { return mRequester; } Q_SIGNALS: void fileNameChanged(const QString &filename); protected: bool event(QEvent *e) override { if (e->type() == QEvent::ToolTipChange) { mRequester->setToolTip(toolTip()); } return QWidget::event(e); } private: QLabel *mIconLabel; FileNameRequester *mRequester; }; class SigEncPage : public QWidget { Q_OBJECT public: explicit SigEncPage(QWidget *parent = nullptr) : QWidget(parent) , mWidget(new SignEncryptWidget) , mOutLayout(new QVBoxLayout) , mOutputLabel{nullptr} , mArchive(false) , mUseOutputDir(false) , mSingleFile{true} { auto mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins({}); auto scrollArea = new Kleo::ScrollArea; mainLayout->addWidget(scrollArea); auto wrapper = new QWidget; scrollArea->setWidget(wrapper); scrollArea->setFrameStyle(0); auto vLay = new QVBoxLayout(wrapper); vLay->setContentsMargins({}); if (!Settings{}.cmsEnabled()) { mWidget->setProtocol(GpgME::OpenPGP); } mWidget->setSignAsText(i18nc("@option:check on SignEncryptPage", "&Sign as:")); mWidget->setEncryptForMeText(i18nc("@option:check on SignEncryptPage", "Encrypt for &me:")); mWidget->setEncryptForOthersText(i18nc("@label on SignEncryptPage", "Encrypt for &others:")); mWidget->setEncryptWithPasswordText(i18nc("@option:check on SignEncryptPage", "Encrypt with &password:")); vLay->addWidget(mWidget); connect(mWidget, &SignEncryptWidget::operationChanged, this, &SigEncPage::checkReady); connect(mWidget, &SignEncryptWidget::keysChanged, this, &SigEncPage::updateFileWidgets); vLay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3); vLay->addLayout(mOutLayout); mPlaceholderWidget = new QLabel(i18nc("@label:textbox", "Please select an action.")); mOutLayout->addWidget(mPlaceholderWidget); mOutputLabel = new QLabel(i18nc("@label on SignEncryptPage", "Output &files/folder:")); auto font = mOutputLabel->font(); font.setWeight(QFont::DemiBold); mOutputLabel->setFont(font); mOutLayout->addWidget(mOutputLabel); createRequesters(mOutLayout); mUseOutputDirChk = new QCheckBox(i18nc("@option:check on SignEncryptPage", "Encrypt / Sign &each file separately.")); mUseOutputDirChk->setToolTip(i18nc("@info:tooltip", "Keep each file separate instead of creating an archive for all.")); mOutLayout->addWidget(mUseOutputDirChk); connect(mUseOutputDirChk, &QCheckBox::toggled, this, [this](bool state) { mUseOutputDir = state; mArchive = !mUseOutputDir && !mSingleFile; updateFileWidgets(); }); auto messageWidget = new KMessageWidget; messageWidget->setMessageType(KMessageWidget::Error); messageWidget->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, this)); messageWidget->setText(i18n("Invalid compliance settings for signing and encrypting files.")); messageWidget->setToolTip(xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant", "<para>You cannot use <application>Kleopatra</application> for signing or encrypting files " "because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>", DeVSCompliance::name(true))); messageWidget->setCloseButtonVisible(false); messageWidget->setVisible(DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()); vLay->addWidget(messageWidget); setMinimumHeight(300); vLay->addStretch(); } void setEncryptionPreset(bool value) { mWidget->setEncryptionChecked(value); } void setSigningPreset(bool value) { mWidget->setSigningChecked(value); } void setArchiveForced(bool archive) { mArchive = archive; setArchiveMutable(!archive); } void setArchiveMutable(bool archive) { mUseOutputDirChk->setVisible(archive); if (archive) { const KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesWizard")); mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false)); } else { mUseOutputDirChk->setChecked(false); } } void setSingleFile(bool singleFile) { mSingleFile = singleFile; mArchive = !mUseOutputDir && !mSingleFile; } bool validatePage() { - if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) { - return false; - } - return mWidget->isComplete(); } std::vector<Key> recipients() const { return mWidget->recipients(); } /* In the future we might find a usecase for multiple * signers */ std::vector<Key> signers() const { const Key k = mWidget->signUserId().parent(); if (!k.isNull()) { return {k}; } return {}; } void done() { mWidget->saveOwnKeys(); if (mUseOutputDirChk->isVisible()) { KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesDialog")); archCfg.writeEntry("LastUseOutputDir", mUseOutputDir); } auto sign = !mWidget->signUserId().isNull(); auto encrypt = !mWidget->selfUserId().isNull() || !mWidget->recipients().empty(); if (!mWidget->validate()) { return; } if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) { KMessageBox::error(topLevelWidget(), xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant", "<para>Sorry! You cannot use <application>Kleopatra</application> for signing or encrypting files " "because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>", DeVSCompliance::name(true))); return; } if (sign && !encrypt && mArchive) { auto status = KMessageBox::warningContinueCancel( this, xi18nc("@info", "<para>Archiving in combination with sign-only currently requires what are known as opaque signatures - " "unlike detached ones, these embed the content in the signature.</para>" "<para>This format is rather unusual. You might want to archive the files separately, " "and then sign the archive as one file with Kleopatra.</para>" "<para>Future versions of Kleopatra are expected to also support detached signatures in this case.</para>"), i18nc("@title:window", "Unusual Signature Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("signencryptfileswizard-archive+sign-only-warning")); if (status != KMessageBox::Continue) { return; } } if (encrypt && !mWidget->encryptSymmetric() && std::ranges::none_of(recipients(), [](const auto &k) { return k.hasSecret(); })) { if (KMessageBox::warningContinueCancel(this, xi18nc("@info", "<para>None of the recipients you are encrypting to seems to be your own.</para>" "<para>This means that you will not be able to decrypt the data anymore, once encrypted.</para>" "<para>Do you want to continue, or cancel to change the recipient selection?</para>"), i18nc("@title:window", "Encrypt-To-Self Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("warn-encrypt-to-non-self"), KMessageBox::Notify | KMessageBox::Dangerous) == KMessageBox::Cancel) { return; } } Q_EMIT finished(); } bool isDeVsAndValid() const { return mWidget->isDeVsAndValid(); } + QString continueButtonText() const + { + return mWidget->continueButtonText(); + } + private: struct RequesterInfo { SignEncryptFilesDialog::KindNames id; QString icon; QString toolTip; QString accessibleName; QString nameFilterBinary; QString nameFilterAscii; }; void createRequesters(QBoxLayout *lay) { static const std::array<RequesterInfo, 6> requestersInfo = {{ { SignEncryptFilesDialog::SignatureCMS, QStringLiteral("document-sign"), i18nc("@info:tooltip", "This is the filename of the S/MIME signature."), i18nc("Lineedit accessible name", "S/MIME signature file"), i18nc("Name filter binary", "S/MIME Signatures (*.p7s)"), i18nc("Name filter ASCII", "S/MIME Signatures (*.p7s *.pem)"), }, { SignEncryptFilesDialog::SignaturePGP, QStringLiteral("document-sign"), i18nc("@info:tooltip", "This is the filename of the detached OpenPGP signature."), i18nc("Lineedit accessible name", "OpenPGP signature file"), i18nc("Name filter binary", "OpenPGP Signatures (*.sig *.pgp)"), i18nc("Name filter ASCII", "OpenPGP Signatures (*.asc *.sig)"), }, { SignEncryptFilesDialog::CombinedPGP, QStringLiteral("document-edit-sign-encrypt"), i18nc("@info:tooltip", "This is the filename of the OpenPGP-signed and encrypted file."), i18nc("Lineedit accessible name", "OpenPGP signed and encrypted file"), i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"), i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"), }, { SignEncryptFilesDialog::EncryptedPGP, QStringLiteral("document-encrypt"), i18nc("@info:tooltip", "This is the filename of the OpenPGP encrypted file."), i18nc("Lineedit accessible name", "OpenPGP encrypted file"), i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"), i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"), }, { SignEncryptFilesDialog::EncryptedCMS, QStringLiteral("document-encrypt"), i18nc("@info:tooltip", "This is the filename of the S/MIME encrypted file."), i18nc("Lineedit accessible name", "S/MIME encrypted file"), i18nc("Name filter binary", "S/MIME Files (*.p7m)"), i18nc("Name filter ASCII", "S/MIME Files (*.p7m *.pem)"), }, { SignEncryptFilesDialog::Directory, QStringLiteral("folder"), i18nc("@info:tooltip", "The resulting files are written to this directory."), i18nc("Lineedit accessible name", "Output directory"), {}, {}, }, }}; if (!mRequesters.empty()) { return; } const bool isAscii = FileOperationsPreferences().addASCIIArmor(); for (const auto &requester : requestersInfo) { const auto id = requester.id; auto requesterWithIcon = new FileNameRequesterWithIcon{id == SignEncryptFilesDialog::Directory ? QDir::Dirs : QDir::Files, this}; requesterWithIcon->setIcon(QIcon::fromTheme(requester.icon)); requesterWithIcon->setToolTip(requester.toolTip); requesterWithIcon->requester()->setAccessibleNameOfLineEdit(requester.accessibleName); requesterWithIcon->setNameFilter(isAscii ? requester.nameFilterAscii : requester.nameFilterBinary); lay->addWidget(requesterWithIcon); connect(requesterWithIcon, &FileNameRequesterWithIcon::fileNameChanged, this, [this, id](const QString &newName) { mOutNames[id] = newName; }); mRequesters.insert(id, requesterWithIcon); } } public: void setOutputNames(const QMap<int, QString> &names) { Q_ASSERT(mOutNames.isEmpty()); for (auto it = std::begin(names); it != std::end(names); ++it) { mRequesters.value(it.key())->setFileName(it.value()); } mOutNames = names; updateFileWidgets(); } QMap<int, QString> outputNames() const { if (!mUseOutputDir) { auto ret = mOutNames; ret.remove(SignEncryptFilesDialog::Directory); return ret; } return mOutNames; } bool encryptSymmetric() const { return mWidget->encryptSymmetric(); } private Q_SLOTS: void updateFileWidgets() { if (mRequesters.isEmpty()) { return; } const std::vector<Key> recipients = mWidget->recipients(); const Key sigKey = mWidget->signUserId().parent(); const bool pgp = mWidget->encryptSymmetric() || std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) { return k.protocol() == Protocol::OpenPGP; }); const bool cms = std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) { return k.protocol() == Protocol::CMS; }); mOutLayout->setEnabled(false); if (cms || pgp || !sigKey.isNull()) { mPlaceholderWidget->setVisible(false); mOutputLabel->setVisible(true); mRequesters[SignEncryptFilesDialog::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS); mRequesters[SignEncryptFilesDialog::EncryptedCMS]->setVisible(!mUseOutputDir && cms); mRequesters[SignEncryptFilesDialog::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp); mRequesters[SignEncryptFilesDialog::EncryptedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() != Protocol::OpenPGP && pgp); mRequesters[SignEncryptFilesDialog::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp); mRequesters[SignEncryptFilesDialog::Directory]->setVisible(mUseOutputDir); auto firstNotHidden = std::find_if(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) { return !w->isHidden(); }); mOutputLabel->setBuddy(*firstNotHidden); } else { mPlaceholderWidget->setVisible(true); mOutputLabel->setVisible(false); std::for_each(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) { w->setVisible(false); }); mOutputLabel->setBuddy(nullptr); } mOutLayout->setEnabled(true); Q_EMIT checkReady(mWidget->currentOp()); } Q_SIGNALS: void finished(); void checkReady(SignEncryptWidget::Operations op); private: SignEncryptWidget *mWidget; QMap<int, QString> mOutNames; QMap<int, FileNameRequesterWithIcon *> mRequesters; QVBoxLayout *mOutLayout; QWidget *mPlaceholderWidget; QCheckBox *mUseOutputDirChk; QLabel *mOutputLabel; bool mArchive; bool mUseOutputDir; bool mSingleFile; }; class SignEncryptResultPage : public Kleo::Crypto::Gui::ResultPage { Q_OBJECT public: explicit SignEncryptResultPage(QWidget *parent = nullptr) : ResultPage(parent) { setTitle(i18nc("@title", "Results")); setSubTitle(i18nc("@title", "Status and progress of the crypto operations is shown here.")); } }; SignEncryptFilesDialog::SignEncryptFilesDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { readConfig(); setWindowTitle(i18nc("@title", "Sign / Encrypt Files")); mSigEncPage = new SigEncPage; mResultPage = new SignEncryptResultPage(this); mResultPage->setVisible(false); auto layout = new QVBoxLayout(this); auto title = new KTitleWidget; title->setText(i18nc("@title:dialog", "Sign / Encrypt Files")); layout->addWidget(title); auto stackedLayout = new QStackedLayout; stackedLayout->addWidget(mSigEncPage); stackedLayout->addWidget(mResultPage); layout->addLayout(stackedLayout); auto buttons = new QDialogButtonBox; QPushButton *labelButton = nullptr; if (DeVSCompliance::isActive()) { /* We use a custom button to display a label next to the buttons. */ labelButton = buttons->addButton(QString(), QDialogButtonBox::ActionRole); /* We style the button so that it looks and acts like a label. */ labelButton->setStyleSheet(QStringLiteral("border: none")); labelButton->setFocusPolicy(Qt::NoFocus); } auto okButton = buttons->addButton(i18nc("@action:button", "Continue"), QDialogButtonBox::ActionRole); auto cancelButton = buttons->addButton(QDialogButtonBox::Cancel); connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject); connect(okButton, &QPushButton::clicked, this, [this]() { mSigEncPage->done(); }); connect(mSigEncPage, &SigEncPage::finished, this, [this, title, okButton, stackedLayout]() { if (stackedLayout->currentIndex() == 0) { stackedLayout->setCurrentIndex(1); Q_EMIT operationPrepared(); title->setText(i18nc("@title:dialog", "Results")); okButton->setText(i18nc("@action:button", "Finish")); } else { accept(); } }); connect(mSigEncPage, &SigEncPage::checkReady, this, [this, okButton, labelButton](const auto op) { - QString label; - switch (op) { - case SignEncryptWidget::Sign: - label = i18nc("@action:button", "Sign"); - break; - case SignEncryptWidget::Encrypt: - label = i18nc("@action:button", "Encrypt"); - break; - case SignEncryptWidget::SignAndEncrypt: - label = i18nc("@action:button", "Sign / Encrypt"); - break; - default:; - }; - if (!label.isEmpty()) { - okButton->setText(label); - if (DeVSCompliance::isActive()) { - const bool de_vs = DeVSCompliance::isCompliant() && mSigEncPage->isDeVsAndValid(); - DeVSCompliance::decorate(okButton, de_vs); - - okButton->setToolTip(DeVSCompliance::name(de_vs)); - labelButton->setText(DeVSCompliance::name(de_vs)); - } + okButton->setText(mSigEncPage->continueButtonText()); + if (mSigEncPage->validatePage() && DeVSCompliance::isActive()) { + const bool de_vs = DeVSCompliance::isCompliant() && mSigEncPage->isDeVsAndValid(); + DeVSCompliance::decorate(okButton, de_vs); + + okButton->setToolTip(DeVSCompliance::name(de_vs)); + labelButton->setText(DeVSCompliance::name(de_vs)); } else { - okButton->setText(i18nc("@action:button", "Next")); + if (labelButton) { + labelButton->setText({}); + } okButton->setIcon(QIcon()); okButton->setStyleSheet(QString()); } okButton->setEnabled(mSigEncPage->validatePage()); }); layout->addWidget(buttons); } SignEncryptFilesDialog::~SignEncryptFilesDialog() { qCDebug(KLEOPATRA_LOG) << this << __func__; writeConfig(); } void SignEncryptFilesDialog::setSigningPreset(bool preset) { mSigEncPage->setSigningPreset(preset); } void SignEncryptFilesDialog::setSigningUserMutable(bool mut) { if (mut == mSigningUserMutable) { return; } mSigningUserMutable = mut; } void SignEncryptFilesDialog::setEncryptionPreset(bool preset) { mSigEncPage->setEncryptionPreset(preset); } void SignEncryptFilesDialog::setEncryptionUserMutable(bool mut) { if (mut == mEncryptionUserMutable) { return; } mEncryptionUserMutable = mut; } void SignEncryptFilesDialog::setArchiveForced(bool archive) { mSigEncPage->setArchiveForced(archive); } void SignEncryptFilesDialog::setArchiveMutable(bool archive) { mSigEncPage->setArchiveMutable(archive); } void SignEncryptFilesDialog::setSingleFile(bool singleFile) { mSigEncPage->setSingleFile(singleFile); } std::vector<Key> SignEncryptFilesDialog::resolvedRecipients() const { return mSigEncPage->recipients(); } std::vector<Key> SignEncryptFilesDialog::resolvedSigners() const { return mSigEncPage->signers(); } void SignEncryptFilesDialog::setTaskCollection(const std::shared_ptr<Kleo::Crypto::TaskCollection> &coll) { mResultPage->setTaskCollection(coll); } void SignEncryptFilesDialog::setOutputNames(const QMap<int, QString> &map) const { mSigEncPage->setOutputNames(map); } QMap<int, QString> SignEncryptFilesDialog::outputNames() const { return mSigEncPage->outputNames(); } bool SignEncryptFilesDialog::encryptSymmetric() const { return mSigEncPage->encryptSymmetric(); } void SignEncryptFilesDialog::readConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard")); const QSize size = dialog.readEntry("Size", QSize(640, 480)); if (size.isValid()) { resize(size); } } void SignEncryptFilesDialog::writeConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard")); dialog.writeEntry("Size", size()); dialog.sync(); } #include "signencryptfilesdialog.moc" #include "moc_signencryptfilesdialog.cpp" diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp index 80779d618..97500bb3e 100644 --- a/src/crypto/gui/signencryptwidget.cpp +++ b/src/crypto/gui/signencryptwidget.cpp @@ -1,1022 +1,1040 @@ /* crypto/gui/signencryptwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "signencryptwidget.h" #include "kleopatra_debug.h" #include "certificatelineedit.h" #include "fileoperationspreferences.h" #include "kleopatraapplication.h" #include "settings.h" #include "unknownrecipientwidget.h" #include "utils/gui-helper.h" #include <QButtonGroup> #include <QCheckBox> #include <QGroupBox> #include <QHBoxLayout> #include <QLabel> #include <QRadioButton> #include <QScrollArea> #include <QScrollBar> #include <QVBoxLayout> #include <Libkleo/Algorithm> #include <Libkleo/Compliance> #include <Libkleo/DefaultKeyFilter> #include <Libkleo/ExpiryChecker> #include <Libkleo/ExpiryCheckerConfig> #include <Libkleo/ExpiryCheckerSettings> #include <Libkleo/Formatting> #include <Libkleo/KeyCache> #include <Libkleo/KeyHelpers> #include <Libkleo/KeyListModel> #include <Libkleo/KeyListSortFilterProxyModel> #include <Libkleo/GnuPG> #include <KConfigGroup> #include <KLocalizedString> #include <KMessageBox> #include <KMessageWidget> #include <KSeparator> #include <KSharedConfig> using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; namespace { class SignCertificateFilter : public DefaultKeyFilter { public: SignCertificateFilter(GpgME::Protocol proto) : DefaultKeyFilter() { setIsBad(DefaultKeyFilter::NotSet); setHasSecret(DefaultKeyFilter::Set); setCanSign(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); if (proto == GpgME::OpenPGP) { setIsOpenPGP(DefaultKeyFilter::Set); } else if (proto == GpgME::CMS) { setIsOpenPGP(DefaultKeyFilter::NotSet); } } }; class EncryptCertificateFilter : public DefaultKeyFilter { public: EncryptCertificateFilter(GpgME::Protocol proto) : DefaultKeyFilter() { setIsBad(DefaultKeyFilter::NotSet); setCanEncrypt(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); if (proto == GpgME::OpenPGP) { setIsOpenPGP(DefaultKeyFilter::Set); } else if (proto == GpgME::CMS) { setIsOpenPGP(DefaultKeyFilter::NotSet); } } }; class EncryptSelfCertificateFilter : public EncryptCertificateFilter { public: EncryptSelfCertificateFilter(GpgME::Protocol proto) : EncryptCertificateFilter(proto) { setRevoked(DefaultKeyFilter::NotSet); setExpired(DefaultKeyFilter::NotSet); setCanEncrypt(DefaultKeyFilter::Set); setHasSecret(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); } }; } class SignEncryptWidget::Private { SignEncryptWidget *const q; public: struct RecipientWidgets { CertificateLineEdit *edit; KMessageWidget *expiryMessage; }; explicit Private(SignEncryptWidget *qq, bool sigEncExclusive) : q{qq} , mModel{AbstractKeyListModel::createFlatKeyListModel(qq)} , mIsExclusive{sigEncExclusive} { } CertificateLineEdit *addRecipientWidget(); /* Inserts a new recipient widget after widget @p after or at the end * if @p after is null. */ CertificateLineEdit *insertRecipientWidget(CertificateLineEdit *after); void recpRemovalRequested(const RecipientWidgets &recipient); void onProtocolChanged(); void updateCheckBoxes(); ExpiryChecker *expiryChecker(); void updateExpiryMessages(KMessageWidget *w, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags); void updateAllExpiryMessages(); public: UserIDSelectionCombo *mSigSelect = nullptr; KMessageWidget *mSignKeyExpiryMessage = nullptr; UserIDSelectionCombo *mSelfSelect = nullptr; KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr; std::vector<RecipientWidgets> mRecpWidgets; QList<UnknownRecipientWidget *> mUnknownWidgets; QList<GpgME::Key> mAddedKeys; QList<KeyGroup> mAddedGroups; QVBoxLayout *mRecpLayout = nullptr; Operations mOp; AbstractKeyListModel *mModel = nullptr; QCheckBox *mSymmetric = nullptr; QCheckBox *mSigChk = nullptr; QLabel *mEncOtherLabel = nullptr; QCheckBox *mEncSelfChk = nullptr; GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol; const bool mIsExclusive; std::unique_ptr<ExpiryChecker> mExpiryChecker; QRadioButton *mPGPRB; QRadioButton *mCMSRB; }; SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive) : QWidget{parent} , d{new Private{this, sigEncExclusive}} { auto lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); d->mModel->useKeyCache(true, KeyList::IncludeGroups); const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool havePublicKeys = !KeyCache::instance()->keys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly(); bool pgpOnly = KeyCache::instance()->pgpOnly(); if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) { auto protocolSelectionLay = new QHBoxLayout; auto protocolLabel = new QLabel(i18nc("@label", "Protocol:")); auto font = protocolLabel->font(); font.setWeight(QFont::DemiBold); protocolLabel->setFont(font); lay->addWidget(protocolLabel); lay->addLayout(protocolSelectionLay); lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3); auto grp = new QButtonGroup; d->mPGPRB = new QRadioButton(i18nc("@option:radio", "OpenPGP")); d->mCMSRB = new QRadioButton(i18nc("@option:radio", "S/MIME")); grp->addButton(d->mPGPRB); grp->addButton(d->mCMSRB); protocolSelectionLay->addWidget(d->mPGPRB); protocolSelectionLay->addWidget(d->mCMSRB); connect(d->mPGPRB, &QRadioButton::toggled, this, [this](bool value) { if (value) { setProtocol(GpgME::OpenPGP); KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", false); } }); connect(d->mCMSRB, &QRadioButton::toggled, this, [this](bool value) { if (value) { setProtocol(GpgME::CMS); KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", true); } }); } /* The signature selection */ { d->mSigChk = new QCheckBox{i18n("&Sign as:"), this}; d->mSigChk->setEnabled(haveSecretKeys); d->mSigChk->setChecked(haveSecretKeys); auto checkFont = d->mSigChk->font(); checkFont.setWeight(QFont::DemiBold); d->mSigChk->setFont(checkFont); lay->addWidget(d->mSigChk); d->mSigSelect = new UserIDSelectionCombo{KeyUsage::Sign, this}; d->mSigSelect->setEnabled(d->mSigChk->isChecked()); lay->addWidget(d->mSigSelect); d->mSignKeyExpiryMessage = new KMessageWidget{this}; d->mSignKeyExpiryMessage->setVisible(false); lay->addWidget(d->mSignKeyExpiryMessage); connect(d->mSigSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() { ownCertificateSelectionRequested(CertificateSelectionDialog::SignOnly, d->mSigSelect); }); connect(d->mSigSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() { updateOp(); d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey); }); connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) { d->mSigSelect->setEnabled(checked); updateOp(); d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey); }); connect(d->mSigSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() { updateOp(); d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey); }); lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3); } // Recipient selection { // Own key d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this}; d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); auto checkFont = d->mEncSelfChk->font(); checkFont.setWeight(QFont::DemiBold); d->mEncSelfChk->setFont(checkFont); lay->addWidget(d->mEncSelfChk); d->mSelfSelect = new UserIDSelectionCombo{KeyUsage::Encrypt, this}; d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked()); lay->addWidget(d->mSelfSelect); d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this}; d->mEncryptToSelfKeyExpiryMessage->setVisible(false); lay->addWidget(d->mEncryptToSelfKeyExpiryMessage); connect(d->mSelfSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() { ownCertificateSelectionRequested(CertificateSelectionDialog::EncryptOnly, d->mSelfSelect); }); connect(d->mSelfSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() { updateOp(); d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey); }); connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) { d->mSelfSelect->setEnabled(checked); }); lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3); // Checkbox for other keys d->mEncOtherLabel = new QLabel(i18nc("@label", "Encrypt for others:"), this); d->mEncOtherLabel->setEnabled(havePublicKeys && !symmetricOnly); d->mEncOtherLabel->setFont(checkFont); lay->addWidget(d->mEncOtherLabel); d->mRecpLayout = new QVBoxLayout; lay->addLayout(d->mRecpLayout); d->addRecipientWidget(); lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3); // Checkbox for password d->mSymmetric = new QCheckBox(i18nc("@option:check", "Encrypt with password:")); d->mSymmetric->setFont(checkFont); d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption", "Additionally to the keys of the recipients you can encrypt your data with a password. " "Anyone who has the password can read the data without any secret key. " "Using a password is <b>less secure</b> then public key cryptography. Even if you pick a very strong password.")); d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly); d->mSymmetric->setEnabled(!publicKeyOnly); lay->addWidget(d->mSymmetric); lay->addWidget(new QLabel(i18nc("@info", "Anyone you share the password with can read the data."))); lay->addStretch(); // Connect it connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) { d->mSelfSelect->setEnabled(checked); updateOp(); d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey); }); connect(d->mSelfSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() { updateOp(); d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey); }); connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); if (d->mIsExclusive) { connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) { if (d->mCurrentProto != GpgME::CMS) { return; } if (value) { d->mSigChk->setChecked(false); } }); connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) { if (d->mCurrentProto != GpgME::CMS) { return; } if (value) { d->mEncSelfChk->setChecked(false); // Copying the vector makes sure that all items are actually deleted. for (const auto &widget : std::vector(d->mRecpWidgets)) { d->recpRemovalRequested(widget); } d->addRecipientWidget(); } }); } // Ensure that the d->mSigChk is aligned together with the encryption check boxes. d->mSigChk->setMinimumWidth(qMax(d->mEncOtherLabel->width(), d->mEncSelfChk->width())); } connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { d->updateCheckBoxes(); d->updateAllExpiryMessages(); }); connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() { d->updateCheckBoxes(); d->mExpiryChecker.reset(); d->updateAllExpiryMessages(); }); if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) { KConfigGroup config(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")); if (config.readEntry("wasCMS", false)) { d->mCMSRB->setChecked(true); setProtocol(GpgME::CMS); } else { d->mPGPRB->setChecked(true); setProtocol(GpgME::OpenPGP); } } if (pgpOnly || !Settings{}.cmsEnabled()) { setProtocol(GpgME::OpenPGP); } loadKeys(); d->onProtocolChanged(); updateOp(); } SignEncryptWidget::~SignEncryptWidget() = default; void SignEncryptWidget::setSignAsText(const QString &text) { d->mSigChk->setText(text); } void SignEncryptWidget::setEncryptForMeText(const QString &text) { d->mEncSelfChk->setText(text); } void SignEncryptWidget::setEncryptForOthersText(const QString &text) { d->mEncOtherLabel->setText(text); } void SignEncryptWidget::setEncryptWithPasswordText(const QString &text) { d->mSymmetric->setText(text); } CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget() { return insertRecipientWidget(nullptr); } CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after) { Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1); const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count(); const RecipientWidgets recipient{new CertificateLineEdit{mModel, KeyUsage::Encrypt, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q}}; recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key")); recipient.edit->setEnabled(!KeyCache::instance()->keys().empty() && !FileOperationsPreferences().symmetricEncryptionOnly()); recipient.expiryMessage->setVisible(false); if (!after) { mEncOtherLabel->setBuddy(recipient.edit); } if (static_cast<unsigned>(index / 2) < mRecpWidgets.size()) { mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient); } else { mRecpWidgets.push_back(recipient); } if (mRecpLayout->count() > 0) { auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget(); Kleo::forceSetTabOrder(prevWidget, recipient.edit); Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage); } else { Kleo::forceSetTabOrder(mEncryptToSelfKeyExpiryMessage, recipient.edit); Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage); } Kleo::forceSetTabOrder(recipient.expiryMessage, mSymmetric); mRecpLayout->insertWidget(index, recipient.edit); mRecpLayout->insertWidget(index + 1, recipient.expiryMessage); connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged); connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged); connect(recipient.edit, &CertificateLineEdit::cleared, q, &SignEncryptWidget::recipientsChanged); connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() { q->certificateSelectionRequested(recipient.edit); }); if (mIsExclusive) { connect(recipient.edit, &CertificateLineEdit::keyChanged, q, [this]() { if (mCurrentProto != GpgME::CMS) { return; } mSigChk->setChecked(false); }); } return recipient.edit; } void SignEncryptWidget::addRecipient(const Key &key) { CertificateLineEdit *certSel = d->addRecipientWidget(); if (!key.isNull()) { certSel->setKey(key); d->mAddedKeys << key; } } void SignEncryptWidget::addRecipient(const KeyGroup &group) { CertificateLineEdit *certSel = d->addRecipientWidget(); if (!group.isNull()) { certSel->setGroup(group); d->mAddedGroups << group; } } void SignEncryptWidget::ownCertificateSelectionRequested(CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo) { CertificateSelectionDialog dialog{this}; dialog.setOptions(CertificateSelectionDialog::Options( // CertificateSelectionDialog::SingleSelection | // CertificateSelectionDialog::SecretKeys | // CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto)) | // options); auto keyFilter = std::make_shared<DefaultKeyFilter>(); keyFilter->setMatchContexts(KeyFilter::Filtering); keyFilter->setIsBad(DefaultKeyFilter::NotSet); if (options & CertificateSelectionDialog::SignOnly) { keyFilter->setName(i18n("Usable for Signing")); keyFilter->setDescription(i18n("Certificates that can be used for signing")); keyFilter->setId(QStringLiteral("CanSignFilter")); } else { keyFilter->setName(i18n("Usable for Encryption")); keyFilter->setDescription(i18n("Certificates that can be used for encryption")); keyFilter->setId(QStringLiteral("CanEncryptFilter")); } dialog.addCustomKeyFilter(keyFilter); dialog.setKeyFilter(keyFilter); if (dialog.exec()) { auto userId = dialog.selectedUserIDs()[0]; auto index = combo->findUserId(userId); if (index == -1) { combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("emblem-error")), Formatting::summaryLine(userId), QVariant::fromValue(userId)); index = combo->combo()->count() - 1; } combo->combo()->setCurrentIndex(index); } recipientsChanged(); } void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit) { CertificateSelectionDialog dlg{this}; dlg.setOptions(CertificateSelectionDialog::Options( // CertificateSelectionDialog::MultiSelection | // CertificateSelectionDialog::EncryptOnly | // CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | // CertificateSelectionDialog::IncludeGroups)); auto keyFilter = std::make_shared<DefaultKeyFilter>(); keyFilter->setMatchContexts(KeyFilter::Filtering); keyFilter->setIsBad(DefaultKeyFilter::NotSet); keyFilter->setName(i18n("Usable for Encryption")); keyFilter->setDescription(i18n("Certificates that can be used for encryption")); keyFilter->setId(QStringLiteral("CanEncryptFilter")); dlg.addCustomKeyFilter(keyFilter); dlg.setKeyFilter(keyFilter); if (!certificateLineEdit->key().isNull()) { const auto key = certificateLineEdit->key(); const auto name = QString::fromUtf8(key.userID(0).name()); const auto email = QString::fromUtf8(key.userID(0).email()); dlg.setStringFilter(!name.isEmpty() ? name : email); } else if (!certificateLineEdit->group().isNull()) { dlg.setStringFilter(certificateLineEdit->group().name()); } else if (!certificateLineEdit->userID().isNull()) { const auto userID = certificateLineEdit->userID(); const auto name = QString::fromUtf8(userID.name()); const auto email = QString::fromUtf8(userID.email()); dlg.setStringFilter(!name.isEmpty() ? name : email); } else { dlg.setStringFilter(certificateLineEdit->text()); } if (dlg.exec()) { const std::vector<UserID> userIds = dlg.selectedUserIDs(); const std::vector<KeyGroup> groups = dlg.selectedGroups(); if (userIds.size() == 0 && groups.size() == 0) { return; } CertificateLineEdit *certWidget = nullptr; for (const auto &userId : userIds) { if (!certWidget) { certWidget = certificateLineEdit; } else { certWidget = d->insertRecipientWidget(certWidget); } certWidget->setUserID(userId); } for (const KeyGroup &group : groups) { if (!certWidget) { certWidget = certificateLineEdit; } else { certWidget = d->insertRecipientWidget(certWidget); } certWidget->setGroup(group); } } recipientsChanged(); } void SignEncryptWidget::clearAddedRecipients() { for (auto w : std::as_const(d->mUnknownWidgets)) { d->mRecpLayout->removeWidget(w); delete w; } for (auto &key : std::as_const(d->mAddedKeys)) { removeRecipient(key); } for (auto &group : std::as_const(d->mAddedGroups)) { removeRecipient(group); } } void SignEncryptWidget::addUnknownRecipient(const char *keyID) { auto unknownWidget = new UnknownRecipientWidget(keyID); d->mUnknownWidgets << unknownWidget; if (d->mRecpLayout->count() > 0) { auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget(); setTabOrder(lastWidget, unknownWidget); } d->mRecpLayout->addWidget(unknownWidget); connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { // Check if any unknown recipient can now be found. for (auto w : std::as_const(d->mUnknownWidgets)) { auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData()); if (key.isNull()) { std::vector<std::string> subids; subids.push_back(std::string(w->keyID().toLatin1().constData())); for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) { key = subkey.parent(); } } if (key.isNull()) { continue; } // Key is now available replace by line edit. qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID(); d->mRecpLayout->removeWidget(w); d->mUnknownWidgets.removeAll(w); delete w; addRecipient(key); } }); } void SignEncryptWidget::recipientsChanged() { const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) { return w.edit->isEmpty(); }); if (!hasEmptyRecpWidget) { d->addRecipientWidget(); } updateOp(); for (const auto &recipient : std::as_const(d->mRecpWidgets)) { if (!recipient.edit->isEditingInProgress() || recipient.edit->isEmpty()) { d->updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey); } } } UserID SignEncryptWidget::signUserId() const { if (d->mSigSelect->isEnabled()) { return d->mSigSelect->currentUserID(); } return UserID(); } UserID SignEncryptWidget::selfUserId() const { if (d->mSelfSelect->isEnabled()) { return d->mSelfSelect->currentUserID(); } return UserID(); } std::vector<Key> SignEncryptWidget::recipients() const { std::vector<Key> ret; for (const auto &recipient : std::as_const(d->mRecpWidgets)) { const auto *const w = recipient.edit; if (!w->isEnabled()) { // If one is disabled, all are disabled. break; } const Key k = w->key(); const KeyGroup g = w->group(); const UserID u = w->userID(); if (!k.isNull()) { ret.push_back(k); } else if (!g.isNull()) { const auto keys = g.keys(); std::copy(keys.begin(), keys.end(), std::back_inserter(ret)); } else if (!u.isNull()) { ret.push_back(u.parent()); } } const Key k = selfUserId().parent(); if (!k.isNull()) { ret.push_back(k); } return ret; } bool SignEncryptWidget::isDeVsAndValid() const { if (!signUserId().isNull() && !DeVSCompliance::userIDIsCompliant(signUserId())) { return false; } if (!selfUserId().isNull() && !DeVSCompliance::userIDIsCompliant(selfUserId())) { return false; } for (const auto &key : recipients()) { if (!DeVSCompliance::keyIsCompliant(key)) { return false; } } return true; } static QString expiryMessage(const ExpiryChecker::Result &result) { if (result.expiration.certificate.isNull()) { return {}; } switch (result.expiration.status) { case ExpiryChecker::Expired: return i18nc("@info", "This certificate is expired."); case ExpiryChecker::ExpiresSoon: { if (result.expiration.duration.count() == 0) { return i18nc("@info", "This certificate expires today."); } else { return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count()); } } case ExpiryChecker::NoSuitableSubkey: if (result.checkFlags & ExpiryChecker::EncryptionKey) { return i18nc("@info", "This certificate cannot be used for encryption."); } else { return i18nc("@info", "This certificate cannot be used for signing."); } case ExpiryChecker::InvalidKey: case ExpiryChecker::InvalidCheckFlags: break; // wrong usage of ExpiryChecker; can be ignored case ExpiryChecker::NotNearExpiry:; } return {}; } void SignEncryptWidget::updateOp() { const std::vector<Key> recp = recipients(); Operations op = NoOperation; if (!signUserId().isNull()) { op |= Sign; } if (!recp.empty() || encryptSymmetric()) { op |= Encrypt; } d->mOp = op; Q_EMIT operationChanged(d->mOp); Q_EMIT keysChanged(); } SignEncryptWidget::Operations SignEncryptWidget::currentOp() const { return d->mOp; } namespace { bool recipientWidgetHasFocus(QWidget *w) { // check if w (or its focus proxy) or a child widget of w has focus return w->hasFocus() || w->isAncestorOf(qApp->focusWidget()); } } void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient) { if (!recipient.edit) { return; } if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) { const int index = mRecpLayout->indexOf(recipient.edit); const auto focusWidget = (index < mRecpLayout->count() - 2) ? // mRecpLayout->itemAt(index + 2)->widget() : mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget(); focusWidget->setFocus(); } mRecpLayout->removeWidget(recipient.expiryMessage); mRecpLayout->removeWidget(recipient.edit); const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) { return r.edit == recipient.edit; }); mRecpWidgets.erase(it); recipient.expiryMessage->deleteLater(); recipient.edit->deleteLater(); } void SignEncryptWidget::removeRecipient(const GpgME::Key &key) { for (const auto &recipient : std::as_const(d->mRecpWidgets)) { const auto editKey = recipient.edit->key(); if (key.isNull() && editKey.isNull()) { d->recpRemovalRequested(recipient); return; } if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) { d->recpRemovalRequested(recipient); return; } } } void SignEncryptWidget::removeRecipient(const KeyGroup &group) { for (const auto &recipient : std::as_const(d->mRecpWidgets)) { const auto editGroup = recipient.edit->group(); if (group.isNull() && editGroup.isNull()) { d->recpRemovalRequested(recipient); return; } if (editGroup.name() == group.name()) { d->recpRemovalRequested(recipient); return; } } } bool SignEncryptWidget::encryptSymmetric() const { return d->mSymmetric->isChecked(); } void SignEncryptWidget::loadKeys() { KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys")); auto cache = KeyCache::instance(); d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString())); d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString())); } void SignEncryptWidget::saveOwnKeys() const { KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys")); auto sigKey = d->mSigSelect->currentKey(); auto encKey = d->mSelfSelect->currentKey(); if (!sigKey.isNull()) { keys.writeEntry("SigningKey", sigKey.primaryFingerprint()); } if (!encKey.isNull()) { keys.writeEntry("EncryptKey", encKey.primaryFingerprint()); } } void SignEncryptWidget::setSigningChecked(bool value) { d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty()); } void SignEncryptWidget::setEncryptionChecked(bool checked) { if (checked) { const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool havePublicKeys = !KeyCache::instance()->keys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly(); d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly); } else { d->mEncSelfChk->setChecked(false); d->mSymmetric->setChecked(false); } } void SignEncryptWidget::setProtocol(GpgME::Protocol proto) { if (d->mCurrentProto == proto) { return; } d->mCurrentProto = proto; if (d->mPGPRB) { d->mPGPRB->setChecked(proto == GpgME::OpenPGP); d->mCMSRB->setChecked(proto == GpgME::CMS); } d->onProtocolChanged(); } void Kleo::SignEncryptWidget::Private::onProtocolChanged() { auto encryptForOthers = q->recipients().size() > 0; mSigSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new SignCertificateFilter(mCurrentProto))); mSelfSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new EncryptSelfCertificateFilter(mCurrentProto))); const auto encFilter = std::shared_ptr<KeyFilter>(new EncryptCertificateFilter(mCurrentProto)); for (const auto &widget : std::vector(mRecpWidgets)) { recpRemovalRequested(widget); } addRecipientWidget(); for (const auto &recipient : std::as_const(mRecpWidgets)) { recipient.edit->setKeyFilter(encFilter); } if (mIsExclusive) { mSymmetric->setDisabled(mCurrentProto == GpgME::CMS); if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) { mSymmetric->setChecked(false); } if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || encryptForOthers)) { mSigChk->setChecked(false); } } } static bool recipientIsOkay(const CertificateLineEdit *edit) { if (!edit->isEnabled() || edit->isEmpty()) { return true; } if (!edit->hasAcceptableInput()) { return false; } if (const auto userID = edit->userID(); !userID.isNull()) { return Kleo::canBeUsedForEncryption(userID.parent()) && !userID.isBad(); } if (const auto key = edit->key(); !key.isNull()) { return Kleo::canBeUsedForEncryption(key); } if (const auto group = edit->group(); !group.isNull()) { return std::ranges::all_of(group.keys(), Kleo::canBeUsedForEncryption); } // we should never reach this point return false; } bool SignEncryptWidget::isComplete() const { + if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) { + return false; + } + if (currentOp() == NoOperation) { return false; } if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signUserId().parent())) { return false; } if (currentOp() & SignEncryptWidget::Encrypt) { if (!selfUserId().isNull() && !Kleo::canBeUsedForEncryption(selfUserId().parent())) { return false; } const bool allOtherRecipientsAreOkay = std::ranges::all_of(d->mRecpWidgets, [](const auto &r) { return recipientIsOkay(r.edit); }); if (!allOtherRecipientsAreOkay) { return false; } } return true; } bool SignEncryptWidget::validate() { CertificateLineEdit *firstUnresolvedRecipient = nullptr; QStringList unresolvedRecipients; for (const auto &recipient : std::as_const(d->mRecpWidgets)) { if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) { if (!firstUnresolvedRecipient) { firstUnresolvedRecipient = recipient.edit; } unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped()); } } if (!unresolvedRecipients.isEmpty()) { KMessageBox::errorList(this, i18n("Could not find a key for the following recipients:"), unresolvedRecipients, i18nc("@title:window", "Failed to find some keys")); } if (firstUnresolvedRecipient) { firstUnresolvedRecipient->setFocus(); } return unresolvedRecipients.isEmpty(); } void SignEncryptWidget::Private::updateCheckBoxes() { const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); mSigChk->setEnabled(haveSecretKeys); mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); if (symmetricOnly) { mEncSelfChk->setChecked(false); mSymmetric->setChecked(true); } } ExpiryChecker *Kleo::SignEncryptWidget::Private::expiryChecker() { if (!mExpiryChecker) { mExpiryChecker.reset(new ExpiryChecker{ExpiryCheckerConfig{}.settings()}); } return mExpiryChecker.get(); } void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags) { messageWidget->setCloseButtonVisible(false); if (userID.isNull()) { messageWidget->setVisible(false); } else if (userID.parent().isExpired()) { messageWidget->setText(i18nc("@info", "This certificate is expired.")); messageWidget->setVisible(true); } else if (userID.isRevoked() || userID.parent().isRevoked()) { messageWidget->setText(i18nc("@info", "This certificate is revoked.")); messageWidget->setVisible(true); } else if (Settings{}.showExpiryNotifications()) { const auto result = expiryChecker()->checkKey(userID.parent(), flags); const auto message = expiryMessage(result); messageWidget->setText(message); messageWidget->setVisible(!message.isEmpty()); } else { messageWidget->setVisible(false); } } void SignEncryptWidget::Private::updateAllExpiryMessages() { updateExpiryMessages(mSignKeyExpiryMessage, q->signUserId(), ExpiryChecker::OwnSigningKey); updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfUserId(), ExpiryChecker::OwnEncryptionKey); for (const auto &recipient : std::as_const(mRecpWidgets)) { if (recipient.edit->isEnabled()) { updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey); } } } +QString SignEncryptWidget::continueButtonText() const +{ + switch (d->mOp) { + case SignEncryptWidget::Sign: + return i18nc("@action:button", "Sign"); + case SignEncryptWidget::Encrypt: + return i18nc("@action:button", "Encrypt"); + case SignEncryptWidget::SignAndEncrypt: + return i18nc("@action:button", "Sign / Encrypt"); + default: + return i18nc("@action:button", "Next"); + }; +} + #include "moc_signencryptwidget.cpp" diff --git a/src/crypto/gui/signencryptwidget.h b/src/crypto/gui/signencryptwidget.h index e8ee5eb69..6ba84efd0 100644 --- a/src/crypto/gui/signencryptwidget.h +++ b/src/crypto/gui/signencryptwidget.h @@ -1,143 +1,147 @@ /* crypto/gui/signencryptwidget.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "dialogs/certificateselectiondialog.h" #include <QList> #include <QWidget> #include <gpgme++/global.h> #include <Libkleo/UserIDSelectionCombo> #include <memory> namespace GpgME { class Key; } namespace Kleo { class CertificateLineEdit; class KeyGroup; class SignEncryptWidget : public QWidget { Q_OBJECT public: enum Operation { NoOperation = 0x00, Sign = 0x01, Encrypt = 0x02, SignAndEncrypt = Sign | Encrypt }; Q_DECLARE_FLAGS(Operations, Operation) /** If exclusive is true, only one protocol can be used, and cms operations can be * done only either as sign or as encrypt */ explicit SignEncryptWidget(QWidget *parent = nullptr, bool exclusive = false); ~SignEncryptWidget() override; /** Overwrite default text with custom text, e.g. with a character marked * as shortcut key. */ void setSignAsText(const QString &text); void setEncryptForMeText(const QString &text); void setEncryptForOthersText(const QString &text); void setEncryptWithPasswordText(const QString &text); /** Returns the list of recipients selected in the dialog * or an empty list if encryption is disabled */ std::vector<GpgME::Key> recipients() const; /** Returns the selected signing key or a null key if signing * is disabled. */ GpgME::UserID signUserId() const; /** Returns the selected encrypt to self key or a null key if * encrypt to self is disabled. */ GpgME::UserID selfUserId() const; /** Returns the operation based on the current selection. */ Operations currentOp() const; /** Whether or not symmetric encryption should also be used. */ bool encryptSymmetric() const; /** Save the currently selected signing and encrypt to self keys. */ void saveOwnKeys() const; /** Return whether or not all keys involved in the operation are compliant with CO_DE_VS, and all keys are valid (i.e. all userIDs have Validity >= Full). */ bool isDeVsAndValid() const; /** Set whether or not signing group should be checked */ void setSigningChecked(bool value); /** Set whether or not encryption group should be checked */ void setEncryptionChecked(bool value); /** Filter for a specific protocol. Use UnknownProtocol for both * S/MIME and OpenPGP */ void setProtocol(GpgME::Protocol protocol); /** Add a recipient with the key key */ void addRecipient(const GpgME::Key &key); /** Add a group of recipients */ void addRecipient(const Kleo::KeyGroup &group); /** Add a placehoder for an unknown key */ void addUnknownRecipient(const char *keyId); /** Remove all Recipients added by keyId or by key. */ void clearAddedRecipients(); /** Remove a Recipient key */ void removeRecipient(const GpgME::Key &key); /** Remove a recipient group */ void removeRecipient(const Kleo::KeyGroup &group); /** Returns true if all required information has been entered. */ bool isComplete() const; /** Returns true if all recipients have been resolved. Otherwise, shows an error message and returns false. */ bool validate(); + /** Text for the "continue" button, depending on the configured operation; + * e.g. "Encrypt", "Sign", "Sign / Encrypt". */ + QString continueButtonText() const; + protected Q_SLOTS: void updateOp(); void recipientsChanged(); void certificateSelectionRequested(CertificateLineEdit *w); void ownCertificateSelectionRequested(Kleo::Dialogs::CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo); protected: void loadKeys(); Q_SIGNALS: /* Emitted when the certificate selection changed the operation * with that selection. e.g. "Sign" or "Sign/Encrypt". * If no crypto operation is selected this returns a null string. */ void operationChanged(Operations op); /* Emitted when the certificate selection might be changed. */ void keysChanged(); private: class Private; const std::unique_ptr<Private> d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(SignEncryptWidget::Operations) } // namespace Kleo diff --git a/src/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp index 1ff1e2413..dc3fd786a 100644 --- a/src/crypto/signencrypttask.cpp +++ b/src/crypto/signencrypttask.cpp @@ -1,1040 +1,1058 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/signencrypttask.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 <config-kleopatra.h> #include "signencrypttask.h" #include <utils/input.h> #include <utils/kleo_assert.h> #include <utils/output.h> #include <utils/path-helper.h> #include <Libkleo/AuditLogEntry> #include <Libkleo/Formatting> #include <Libkleo/KleoException> #include <Libkleo/Stl_Util> #include <QGpgME/Debug> #include <QGpgME/EncryptArchiveJob> #include <QGpgME/EncryptJob> #include <QGpgME/Protocol> #include <QGpgME/SignArchiveJob> #include <QGpgME/SignEncryptArchiveJob> #include <QGpgME/SignEncryptJob> #include <QGpgME/SignJob> #include <gpgme++/encryptionresult.h> #include <gpgme++/key.h> #include <gpgme++/signingresult.h> #include <KLocalizedString> #include "kleopatra_debug.h" #include <QFileInfo> #include <QPointer> using namespace Kleo; using namespace Kleo::Crypto; using namespace GpgME; namespace { class ErrorResult : public Task::Result { public: ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog) : Task::Result() , m_sign(sign) , m_encrypt(encrypt) , m_error(err) , m_errString(errStr) , m_inputLabel(input) , m_outputLabel(output) , m_auditLog(auditLog) { } QString overview() const override; QString details() const override; GpgME::Error error() const override { return m_error; } QString errorString() const override { return m_errString; } AuditLogEntry auditLog() const override { return m_auditLog; } private: const bool m_sign; const bool m_encrypt; const Error m_error; const QString m_errString; const QString m_inputLabel; const QString m_outputLabel; const AuditLogEntry m_auditLog; }; namespace { struct LabelAndError { QString label; QString errorString; QStringList fileNames; }; } class SignEncryptFilesResult : public Task::Result { public: SignEncryptFilesResult(const SigningResult &sr, const EncryptionResult &er, const LabelAndError &input, const LabelAndError &output, const AuditLogEntry &auditLog) : Task::Result() , m_sresult(sr) , m_eresult(er) , m_input{input} , m_output{output} , m_auditLog(auditLog) { qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString << "\noutputError:" << m_output.errorString; Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull()); } QString overview() const override; QString details() const override; GpgME::Error error() const override; QString errorString() const override; AuditLogEntry auditLog() const override; private: const SigningResult m_sresult; const EncryptionResult m_eresult; const LabelAndError m_input; const LabelAndError m_output; const AuditLogEntry m_auditLog; }; QString formatResultLine(const QStringList &inputs, const QString &output, bool sign, bool encrypt, bool signingFailed, bool encryptionFailed, const GpgME::Error &error) { Q_ASSERT(inputs.size() > 0); Q_ASSERT(sign || encrypt); if (error.isCanceled()) { if (sign && encrypt) { return i18nc("@info", "Signing and encryption canceled."); } if (sign) { return i18nc("@info", "Signing canceled."); } return i18nc("@info", "Encryption canceled."); } if (signingFailed && encryptionFailed) { if (inputs.size() == 1) { return xi18nc("@info Failed to sign and encrypt <file>: <reason>", "Failed to sign and encrypt <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>", inputs[0], Formatting::errorAsString(error)); } if (inputs.size() == 2) { return xi18nc("@info Failed to sign and encrypt <file> and <file>: <reason>", "Failed to sign and encrypt <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>", inputs[0], inputs[1], Formatting::errorAsString(error)); } return xi18ncp("@info Failed to sign and encrypt <file> and <n> other(s): <reason>", "Failed to sign and encrypt <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>", "Failed to sign and encrypt <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>", inputs.size() - 1, inputs[0], Formatting::errorAsString(error)); } if (signingFailed) { if (inputs.size() == 1) { return xi18nc("@info Failed to sign <file>: <reason>", "Failed to sign <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>", inputs[0], Formatting::errorAsString(error)); } if (inputs.size() == 2) { return xi18nc("@info Failed to sign <file> and <file>: <reason>", "Failed to sign <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>", inputs[0], inputs[1], Formatting::errorAsString(error)); } return xi18ncp("@info Failed to sign <file> and <n> other(s): <reason>", "Failed to sign <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>", "Failed to sign <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>", inputs.size() - 1, inputs[0], Formatting::errorAsString(error)); } if (encryptionFailed) { if (inputs.size() == 1) { return xi18nc("@info Failed to encrypt <file>: <reason>", "Failed to encrypt <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>", inputs[0], Formatting::errorAsString(error)); } if (inputs.size() == 2) { return xi18nc("@info Failed to encrypt <file> and <file>: <reason>", "Failed to encrypt <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>", inputs[0], inputs[1], Formatting::errorAsString(error)); } return xi18ncp("@info Failed to encrypt <file> and <n> other(s): <reason>", "Failed to encrypt <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>", "Failed to encrypt <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>", inputs.size() - 1, inputs[0], Formatting::errorAsString(error)); } if (sign && encrypt) { if (inputs.size() == 1) { return xi18nc("@info Successfully signed and encrypted <file> and saved it as <file>.", "Successfully signed and encrypted <filename>%1</filename> and saved it as <filename>%2</filename>.", inputs[0], output); } if (inputs.size() == 2) { return xi18nc("@info Successfully signed and encrypted <file> and <file> and saved it as <file>.", "Successfully signed and encrypted <filename>%1</filename> and <filename>%2</filename> and saved it as <filename>%3</filename>.", inputs[0], inputs[1], output); } return xi18ncp("@info Successfully signed and encrypted <file> and <n> other(s) as <file>.", "Successfully signed and encrypted <filename>%2</filename> and %1 other and saved it as <filename>%3</filename>.", "Successfully signed and encrypted <filename>%2</filename> and %1 others and saved it as <filename>%3</filename>.", inputs.size() - 1, inputs[0], output); } if (sign) { if (inputs.size() == 1) { return xi18nc("@info Successfully signed <file> and saved the signature in <file>.", "Successfully signed <filename>%1</filename> and saved the signature in <filename>%2</filename>.", inputs[0], output); } if (inputs.size() == 2) { return xi18nc("@info Successfully signed <file> and <file> and saved the signature in <file>.", "Successfully signed <filename>%1</filename> and <filename>%2</filename> and saved the signature in <filename>%3</filename>.", inputs[0], inputs[1], output); } return xi18ncp("@info Successfully signed <file> and <n> other(s) and saved the signature in <file>.", "Successfully signed <filename>%2</filename> and %1 other and saved the signature in <filename>%3</filename>.", "Successfully signed <filename>%2</filename> and %1 others and saved the signature in <filename>%3</filename>.", inputs.size() - 1, inputs[0], output); } if (inputs.size() == 1) { return xi18nc("@info Successfully encrypted <file> and saved it as <file>.", "Successfully encrypted <filename>%1</filename> and saved it as <filename>%2</filename>.", inputs[0], output); } if (inputs.size() == 2) { return xi18nc("@info Successfully encrypted <file> and <file> and saved it as <file>.", "Successfully encrypted <filename>%1</filename> and <filename>%2</filename> and saved it as <filename>%3</filename>.", inputs[0], inputs[1], output); } return xi18ncp("@info Successfully encrypted <file> and <n> other(s) and saved it as <file>.", "Successfully encrypted <filename>%2</filename> and %1 other and saved it as <filename>%3</filename>.", "Successfully encrypted <filename>%2</filename> and %1 others and saved it as <filename>%3</filename>.", inputs.size() - 1, inputs[0], output); } static QString escape(QString s) { s = s.toHtmlEscaped(); s.replace(QLatin1Char('\n'), QStringLiteral("<br>")); return s; } static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError) { const Error err = result.error(); if (err.code() == GPG_ERR_EIO) { if (!inputError.isEmpty()) { return i18n("Input error: %1", escape(inputError)); } else if (!outputError.isEmpty()) { return i18n("Output error: %1", escape(outputError)); } } if (err || err.isCanceled()) { return Formatting::errorAsString(err).toHtmlEscaped(); } return QString(); } static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError) { const Error err = result.error(); if (err.code() == GPG_ERR_EIO) { if (!inputError.isEmpty()) { return i18n("Input error: %1", escape(inputError)); } else if (!outputError.isEmpty()) { return i18n("Output error: %1", escape(outputError)); } } if (err || err.isCanceled()) { return Formatting::errorAsString(err).toHtmlEscaped(); } return {}; } } QString ErrorResult::overview() const { Q_ASSERT(m_error || m_error.isCanceled()); Q_ASSERT(m_sign || m_encrypt); return formatResultLine({m_inputLabel}, m_outputLabel, m_sign, m_encrypt, true, true, m_error); } QString ErrorResult::details() const { return m_errString; } class SignEncryptTask::Private { friend class ::Kleo::Crypto::SignEncryptTask; SignEncryptTask *const q; public: explicit Private(SignEncryptTask *qq); private: QString inputLabel() const; QString outputLabel() const; bool removeExistingOutputFile(); void startSignEncryptJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto); void startSignEncryptArchiveJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto); std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto); std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog); private: void slotResult(const SigningResult &); void slotResult(const SigningResult &, const EncryptionResult &); void slotResult(const EncryptionResult &); void slotResult(const QGpgME::Job *, const SigningResult &, const EncryptionResult &); private: std::shared_ptr<Input> input; std::shared_ptr<Output> output; QStringList inputFileNames; QString outputFileName; std::vector<Key> signers; std::vector<Key> recipients; SignEncryptTask::DataSource dataSource = SignEncryptTask::DataSource::Files; bool sign : 1; bool encrypt : 1; bool detached : 1; bool symmetric : 1; bool clearsign : 1; bool archive : 1; QPointer<QGpgME::Job> job; QString labelText; std::shared_ptr<OverwritePolicy> m_overwritePolicy; }; SignEncryptTask::Private::Private(SignEncryptTask *qq) : q{qq} , sign{true} , encrypt{true} , detached{false} , clearsign{false} , archive{false} , m_overwritePolicy{new OverwritePolicy{OverwritePolicy::Ask}} { q->setAsciiArmor(true); } std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog) { return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, inputLabel(), outputLabel(), auditLog)); } SignEncryptTask::SignEncryptTask(QObject *p) : Task(p) , d(new Private(this)) { } SignEncryptTask::~SignEncryptTask() { } void SignEncryptTask::setInputFileName(const QString &fileName) { kleo_assert(!d->job); kleo_assert(!fileName.isEmpty()); d->inputFileNames = QStringList(fileName); } void SignEncryptTask::setInputFileNames(const QStringList &fileNames) { kleo_assert(!d->job); kleo_assert(!fileNames.empty()); d->inputFileNames = fileNames; } void SignEncryptTask::setInput(const std::shared_ptr<Input> &input) { kleo_assert(!d->job); kleo_assert(input); d->input = input; } void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output) { kleo_assert(!d->job); kleo_assert(output); d->output = output; } void SignEncryptTask::setOutputFileName(const QString &fileName) { kleo_assert(!d->job); kleo_assert(!fileName.isEmpty()); d->outputFileName = fileName; } QString SignEncryptTask::outputFileName() const { return d->outputFileName; } void SignEncryptTask::setSigners(const std::vector<Key> &signers) { kleo_assert(!d->job); d->signers = signers; } void SignEncryptTask::setRecipients(const std::vector<Key> &recipients) { kleo_assert(!d->job); d->recipients = recipients; } void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy) { kleo_assert(!d->job); d->m_overwritePolicy = policy; } void SignEncryptTask::setSign(bool sign) { kleo_assert(!d->job); d->sign = sign; } void SignEncryptTask::setEncrypt(bool encrypt) { kleo_assert(!d->job); d->encrypt = encrypt; } void SignEncryptTask::setDetachedSignature(bool detached) { kleo_assert(!d->job); d->detached = detached; } bool SignEncryptTask::detachedSignatureEnabled() const { return d->detached; } void SignEncryptTask::setEncryptSymmetric(bool symmetric) { kleo_assert(!d->job); d->symmetric = symmetric; } void SignEncryptTask::setClearsign(bool clearsign) { kleo_assert(!d->job); d->clearsign = clearsign; } void SignEncryptTask::setCreateArchive(bool archive) { kleo_assert(!d->job); d->archive = archive; } Protocol SignEncryptTask::protocol() const { if (d->sign && !d->signers.empty()) { return d->signers.front().protocol(); } if (d->encrypt || d->symmetric) { if (!d->recipients.empty()) { return d->recipients.front().protocol(); } else { return GpgME::OpenPGP; // symmetric OpenPGP encryption } } throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Cannot determine protocol for task")); } QString SignEncryptTask::label() const { if (!d->labelText.isEmpty()) { return d->labelText; } return d->inputLabel(); } QString SignEncryptTask::tag() const { return Formatting::displayName(protocol()); } unsigned long long SignEncryptTask::inputSize() const { return d->input ? d->input->size() : 0U; } static bool archiveJobsCanBeUsed(GpgME::Protocol protocol) { return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported(); } void SignEncryptTask::doStart() { kleo_assert(!d->job); if (d->sign) { kleo_assert(!d->signers.empty()); if (d->archive) { kleo_assert(!d->detached && !d->clearsign); } } const auto proto = protocol(); if (d->archive && archiveJobsCanBeUsed(proto)) { d->startSignEncryptArchiveJob(proto); } else { d->startSignEncryptJob(proto); } } QString SignEncryptTask::Private::inputLabel() const { if (input) { return input->label(); } if (!inputFileNames.empty()) { const auto firstFile = QFileInfo{inputFileNames.front()}.fileName(); return inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile); } return {}; } QString SignEncryptTask::Private::outputLabel() const { return output ? output->label() : QFileInfo{outputFileName}.fileName(); } bool SignEncryptTask::Private::removeExistingOutputFile() { if (QFile::exists(outputFileName)) { bool fileRemoved = false; // we should already have asked the user for overwrite permission if (m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Overwrite)) { qCDebug(KLEOPATRA_LOG) << __func__ << "going to remove file for overwriting" << outputFileName; fileRemoved = QFile::remove(outputFileName); if (!fileRemoved) { qCDebug(KLEOPATRA_LOG) << __func__ << "removing file to overwrite failed"; } } else { qCDebug(KLEOPATRA_LOG) << __func__ << "we have no permission to overwrite" << outputFileName; } if (!fileRemoved) { QMetaObject::invokeMethod( q, [this]() { slotResult(nullptr, SigningResult{}, EncryptionResult{Error::fromCode(GPG_ERR_EEXIST)}); }, Qt::QueuedConnection); return false; } } return true; } void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto) { #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO if (proto == GpgME::OpenPGP) { // either input and output are both set (e.g. when encrypting the notepad), // or they are both unset (when encrypting files) kleo_assert((!input && !output) || (input && output)); } else { kleo_assert(input); if (!output) { output = Output::createFromFile(outputFileName, m_overwritePolicy); } } #else kleo_assert(input); if (!output) { output = Output::createFromFile(outputFileName, m_overwritePolicy); } #endif if (encrypt || symmetric) { Context::EncryptionFlags flags{Context::None}; if (proto == GpgME::OpenPGP) { flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust); } if (symmetric) { flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric); qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag"; } if (sign) { std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto); kleo_assert(job.get()); #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO if (proto == GpgME::OpenPGP && !input && !output) { kleo_assert(inputFileNames.size() == 1); job->setSigners(signers); job->setRecipients(recipients); job->setInputFile(inputFileNames.front()); job->setOutputFile(outputFileName); job->setEncryptionFlags(flags); if (!removeExistingOutputFile()) { return; } job->startIt(); } else { if (inputFileNames.size() == 1) { job->setFileName(inputFileNames.front()); } job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags); } #else if (inputFileNames.size() == 1) { job->setFileName(inputFileNames.front()); } job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags); #endif this->job = job.release(); } else { std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto); kleo_assert(job.get()); #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO if (proto == GpgME::OpenPGP && !input && !output) { kleo_assert(inputFileNames.size() == 1); job->setRecipients(recipients); job->setInputFile(inputFileNames.front()); job->setOutputFile(outputFileName); job->setEncryptionFlags(flags); if (!removeExistingOutputFile()) { return; } job->startIt(); } else { if (inputFileNames.size() == 1) { job->setFileName(inputFileNames.front()); } job->start(recipients, input->ioDevice(), output->ioDevice(), flags); } #else if (inputFileNames.size() == 1) { job->setFileName(inputFileNames.front()); } job->start(recipients, input->ioDevice(), output->ioDevice(), flags); #endif this->job = job.release(); } } else if (sign) { std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto); kleo_assert(job.get()); kleo_assert(!(detached && clearsign)); const GpgME::SignatureMode sigMode = detached ? GpgME::Detached : clearsign ? GpgME::Clearsigned : GpgME::NormalSignatureMode; #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO if (proto == GpgME::OpenPGP && !input && !output) { kleo_assert(inputFileNames.size() == 1); job->setSigners(signers); job->setInputFile(inputFileNames.front()); job->setOutputFile(outputFileName); job->setSigningFlags(sigMode); if (QFile::exists(outputFileName) && m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Append)) { job->setAppendSignature(true); } else if (!removeExistingOutputFile()) { return; } job->startIt(); } else { job->start(signers, input->ioDevice(), output->ioDevice(), sigMode); } #else job->start(signers, input->ioDevice(), output->ioDevice(), sigMode); #endif this->job = job.release(); } else { kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!"); } } void SignEncryptTask::cancel() { qCDebug(KLEOPATRA_LOG) << this << __func__; if (d->job) { d->job->slotCancel(); } } std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(signJob.get()); connect(signJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); connect(signJob.get(), &QGpgME::SignJob::result, q, [this](const GpgME::SigningResult &signingResult, const QByteArray &) { slotResult(signingResult); }); return signJob; } std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(signEncryptJob.get()); connect(signEncryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); connect(signEncryptJob.get(), &QGpgME::SignEncryptJob::result, q, [this](const GpgME::SigningResult &signingResult, const GpgME::EncryptionResult &encryptionResult) { slotResult(signingResult, encryptionResult); }); return signEncryptJob; } std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(encryptJob.get()); connect(encryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); connect(encryptJob.get(), &QGpgME::EncryptJob::result, q, [this](const GpgME::EncryptionResult &encryptionResult) { slotResult(encryptionResult); }); return encryptJob; } void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto) { kleo_assert(!input); kleo_assert(!output); const auto baseDirectory = heuristicBaseDirectory(inputFileNames); if (baseDirectory.isEmpty()) { throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n')))); } qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory; const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames); const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()}; qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths; if (encrypt || symmetric) { Context::EncryptionFlags flags{Context::None}; if (proto == GpgME::OpenPGP) { flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust); } if (symmetric) { flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric); qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag"; } if (sign) { labelText = i18nc("@info", "Creating signed and encrypted archive ..."); std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto); kleo_assert(job.get()); job->setBaseDirectory(baseDirectory); job->setSigners(signers); job->setRecipients(recipients); job->setInputPaths(relativePaths); job->setOutputFile(outputFileName); job->setEncryptionFlags(flags); if (!removeExistingOutputFile()) { return; } job->startIt(); this->job = job.release(); } else { labelText = i18nc("@info", "Creating encrypted archive ..."); std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto); kleo_assert(job.get()); job->setBaseDirectory(baseDirectory); job->setRecipients(recipients); job->setInputPaths(relativePaths); job->setOutputFile(outputFileName); job->setEncryptionFlags(flags); if (!removeExistingOutputFile()) { return; } job->startIt(); this->job = job.release(); } } else if (sign) { labelText = i18nc("@info", "Creating signed archive ..."); std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto); kleo_assert(job.get()); job->setBaseDirectory(baseDirectory); job->setSigners(signers); job->setInputPaths(relativePaths); job->setOutputFile(outputFileName); if (!removeExistingOutputFile()) { return; } job->startIt(); this->job = job.release(); } else { kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!"); } } std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor())); auto job = signJob.get(); kleo_assert(job); connect(job, &QGpgME::SignArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); connect(job, &QGpgME::SignArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult) { slotResult(job, signResult, EncryptionResult{}); }); return signJob; } std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor())); auto job = signEncryptJob.get(); kleo_assert(job); connect(job, &QGpgME::SignEncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); connect(job, &QGpgME::SignEncryptArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) { slotResult(job, signResult, encryptResult); }); return signEncryptJob; } std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor())); auto job = encryptJob.get(); kleo_assert(job); connect(job, &QGpgME::EncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) { slotResult(job, SigningResult{}, encryptResult); }); return encryptJob; } void SignEncryptTask::Private::slotResult(const SigningResult &result) { slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), result, EncryptionResult{}); } void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult) { slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), sresult, eresult); } void SignEncryptTask::Private::slotResult(const EncryptionResult &result) { slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), SigningResult{}, result); } void SignEncryptTask::Private::slotResult(const QGpgME::Job *job, const SigningResult &sresult, const EncryptionResult &eresult) { qCDebug(KLEOPATRA_LOG) << q << __func__ << "job:" << job << "signing result:" << QGpgME::toLogString(sresult) << "encryption result:" << QGpgME::toLogString(eresult); const AuditLogEntry auditLog = AuditLogEntry::fromJob(job); if (input && input->failed()) { if (output) { output->cancel(); } q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape(input->errorString())), auditLog)); return; } else if (sresult.error().code() || eresult.error().code()) { if (output) { output->cancel(); } if (!outputFileName.isEmpty() && eresult.error().code() != GPG_ERR_EEXIST) { // ensure that the output file is removed if the task was canceled or an error occurred; // unless a "file exists" error occurred because this means that the file with the name // of outputFileName wasn't created as result of this task if (QFile::exists(outputFileName)) { qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "after error or cancel"; if (!QFile::remove(outputFileName)) { qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "failed"; } } } } else { try { kleo_assert(!sresult.isNull() || !eresult.isNull()); if (output) { output->finalize(); } if (input) { input->finalize(); } } catch (const GpgME::Exception &e) { q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog)); return; } } const LabelAndError inputInfo{inputLabel(), input ? input->errorString() : QString{}, inputFileNames}; const LabelAndError outputInfo{outputLabel(), output ? output->errorString() : QString{}, {}}; auto result = std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, inputInfo, outputInfo, auditLog)); result->setDataSource(dataSource); q->emitResult(result); } QString SignEncryptFilesResult::overview() const { if (dataSource() == Task::Notepad) { if (!m_sresult.isNull() && m_sresult.error()) { return i18nc("@info", "Failed to sign the notepad: %1", Formatting::errorAsString(m_sresult.error())); } if (!m_eresult.isNull() && m_eresult.error()) { return i18nc("@info", "Failed to encrypt the notepad: %1", Formatting::errorAsString(m_eresult.error())); } if (!m_sresult.isNull() && !m_eresult.isNull()) { return i18nc("@info", "Successfully encrypted and signed the notepad"); } else if (!m_eresult.isNull()) { return i18nc("@info", "Successfully encrypted the notepad"); } else { return i18nc("@info", "Successfully signed the notepad"); } return {}; } + if (dataSource() == Task::Clipboard) { + if (!m_sresult.isNull() && m_sresult.error()) { + return i18nc("@info", "Failed to sign the clipboard: %1", Formatting::errorAsString(m_sresult.error())); + } + if (!m_eresult.isNull() && m_eresult.error()) { + return i18nc("@info", "Failed to encrypt the clipboard: %1", Formatting::errorAsString(m_eresult.error())); + } + + if (!m_sresult.isNull() && !m_eresult.isNull()) { + return i18nc("@info", "Successfully encrypted and signed the clipboard"); + } else if (!m_eresult.isNull()) { + return i18nc("@info", "Successfully encrypted the clipboard"); + } else { + return i18nc("@info", "Successfully signed the clipboard"); + } + return {}; + } + return formatResultLine(m_input.fileNames, m_output.label, !m_sresult.isNull(), !m_eresult.isNull(), m_sresult.error(), m_eresult.error(), m_sresult.error().code() ? m_sresult.error() : m_eresult.error()); } QString SignEncryptFilesResult::details() const { return errorString(); } GpgME::Error SignEncryptFilesResult::error() const { if (m_sresult.error().code()) { return m_sresult.error(); } if (m_eresult.error().code()) { return m_eresult.error(); } return {}; } QString SignEncryptFilesResult::errorString() const { const bool sign = !m_sresult.isNull(); const bool encrypt = !m_eresult.isNull(); kleo_assert(sign || encrypt); if (sign && encrypt) { return m_sresult.error().code() ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) : m_eresult.error().code() ? makeResultDetails(m_eresult, m_input.errorString, m_output.errorString) : QString(); } return sign ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) // : makeResultDetails(m_eresult, m_input.errorString, m_output.errorString); } AuditLogEntry SignEncryptFilesResult::auditLog() const { return m_auditLog; } void SignEncryptTask::setDataSource(Task::DataSource dataSource) { d->dataSource = dataSource; } #include "moc_signencrypttask.cpp" diff --git a/src/dialogs/signencryptclipboarddialog.cpp b/src/dialogs/signencryptclipboarddialog.cpp new file mode 100644 index 000000000..9d029c55e --- /dev/null +++ b/src/dialogs/signencryptclipboarddialog.cpp @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2024 g10 Code GmbH +// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "signencryptclipboarddialog.h" + +#include "crypto/gui/resultpage.h" +#include "crypto/gui/signencryptwidget.h" +#include "crypto/signencrypttask.h" +#include "crypto/task.h" +#include "crypto/taskcollection.h" +#include "utils/input.h" +#include "utils/output.h" +#include "utils/scrollarea.h" + +#include <Libkleo/Compliance> + +#include <gpgme++/key.h> + +#include <KLocalizedString> +#include <KMessageBox> + +#include <QApplication> +#include <QClipboard> +#include <QDialogButtonBox> +#include <QPushButton> +#include <QStackedLayout> +#include <QTimer> +#include <QVBoxLayout> + +using namespace Kleo; +using namespace Kleo::Crypto; + +class SignEncryptPage : public QWidget +{ +public: + explicit SignEncryptPage(QWidget *parent = nullptr) + : QWidget(parent) + { + auto mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins({}); + auto scrollArea = new Kleo::ScrollArea; + mainLayout->addWidget(scrollArea); + + auto wrapper = new QWidget; + scrollArea->setWidget(wrapper); + + scrollArea->setFrameStyle(0); + auto vLay = new QVBoxLayout(wrapper); + vLay->setContentsMargins({}); + m_widget = new SignEncryptWidget(this, true); + vLay->addWidget(m_widget); + } + + std::vector<GpgME::Key> recipients() const + { + return m_widget->recipients(); + } + + GpgME::Key signer() const + { + const auto key = m_widget->signUserId().parent(); + if (!key.isNull()) { + return key; + } + return {}; + } + + SignEncryptWidget *signEncryptWidget() const + { + return m_widget; + } + +private: + SignEncryptWidget *m_widget; +}; + +SignEncryptClipboardDialog::SignEncryptClipboardDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(i18nc("@title:dialog", "Sign/Encrypt Clipboard")); + auto layout = new QVBoxLayout(this); + auto stackedLayout = new QStackedLayout; + + auto signEncryptPage = new SignEncryptPage; + stackedLayout->addWidget(signEncryptPage); + + auto resultPage = new Kleo::Crypto::Gui::ResultPage; + stackedLayout->addWidget(resultPage); + + layout->addLayout(stackedLayout); + + auto buttons = new QDialogButtonBox; + + QPushButton *labelButton = nullptr; + + if (DeVSCompliance::isActive()) { + /* We use a custom button to display a label next to the + buttons. */ + labelButton = buttons->addButton(QString(), QDialogButtonBox::ActionRole); + /* We style the button so that it looks and acts like a + label. */ + labelButton->setStyleSheet(QStringLiteral("border: none")); + labelButton->setFocusPolicy(Qt::NoFocus); + } + + auto okButton = buttons->addButton(i18nc("@action:button", "Continue"), QDialogButtonBox::ActionRole); + buttons->addButton(QDialogButtonBox::Cancel); + + layout->addWidget(buttons); + + connect(signEncryptPage->signEncryptWidget(), &SignEncryptWidget::operationChanged, this, [okButton, signEncryptPage, labelButton]() { + okButton->setText(signEncryptPage->signEncryptWidget()->continueButtonText()); + if (signEncryptPage->signEncryptWidget()->isComplete() && DeVSCompliance::isActive()) { + const bool de_vs = DeVSCompliance::isCompliant() && signEncryptPage->signEncryptWidget()->isDeVsAndValid(); + DeVSCompliance::decorate(okButton, de_vs); + + okButton->setToolTip(DeVSCompliance::name(de_vs)); + labelButton->setText(DeVSCompliance::name(de_vs)); + } else { + if (labelButton) { + labelButton->setText({}); + } + okButton->setIcon(QIcon()); + okButton->setStyleSheet(QString()); + } + okButton->setEnabled(signEncryptPage->signEncryptWidget()->isComplete()); + }); + + connect(okButton, &QPushButton::clicked, this, [this, signEncryptPage, stackedLayout, resultPage, okButton]() { + if (stackedLayout->currentIndex() == 0) { + m_task = std::make_shared<SignEncryptTask>(); + m_task->setDataSource(Task::Clipboard); + auto output = Output::createFromClipboard(); + m_task->setInput(m_input); + m_task->setOutput(output); + + auto recipients = signEncryptPage->recipients(); + auto signer = signEncryptPage->signer(); + + m_task->setRecipients(recipients); + m_task->setEncrypt(recipients.size() > 0); + m_task->setSigners({signer}); + m_task->setSign(!signer.isNull()); + m_task->setClearsign(!signer.isNull() && recipients.size() == 0 && signer.protocol() == GpgME::OpenPGP); + m_task->setEncryptSymmetric(signEncryptPage->signEncryptWidget()->encryptSymmetric()); + m_task->setAsciiArmor(true); + m_task->start(); + + stackedLayout->setCurrentIndex(1); + okButton->setText(i18nc("@action:button", "Finish")); + + std::shared_ptr<TaskCollection> coll(new TaskCollection); + coll->setTasks({m_task}); + resultPage->setTaskCollection(coll); + } else { + accept(); + } + }); + + QTimer::singleShot(100, this, [this]() { + if (qApp->clipboard()->text().isEmpty()) { + KMessageBox::information(this, + i18nc("@info", "The clipboard does not contain data that can be encrypted"), + i18nc("@title:dialog", "Sign/Encrypt Clipboard")); + reject(); + } else { + m_input = Input::createFromClipboard(); + } + }); +} + +SignEncryptClipboardDialog::~SignEncryptClipboardDialog() +{ + if (m_task) { + m_task->cancel(); + } +} diff --git a/src/dialogs/signencryptclipboarddialog.h b/src/dialogs/signencryptclipboarddialog.h new file mode 100644 index 000000000..75b4ef2d4 --- /dev/null +++ b/src/dialogs/signencryptclipboarddialog.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2024 g10 Code GmbH +// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <QDialog> + +namespace Kleo +{ +class Input; +namespace Crypto +{ +class SignEncryptTask; +} +} + +class SignEncryptClipboardDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SignEncryptClipboardDialog(QWidget *parent = nullptr); + ~SignEncryptClipboardDialog(); + + void setPlainText(const QString &plainText); + +private: + QString m_plainText; + std::shared_ptr<Kleo::Crypto::SignEncryptTask> m_task; + std::shared_ptr<Kleo::Input> m_input; +}; diff --git a/src/systrayicon.cpp b/src/systrayicon.cpp index 4317a0ce9..fe399f8d6 100644 --- a/src/systrayicon.cpp +++ b/src/systrayicon.cpp @@ -1,225 +1,224 @@ /* -*- mode: c++; c-basic-offset:4 -*- systemtrayicon.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 <config-kleopatra.h> #include "systrayicon.h" #ifndef QT_NO_SYSTEMTRAYICON #include "kleopatraapplication.h" #include "mainwindow.h" #include <smartcard/readerstatus.h> #include <utils/clipboardmenu.h> #include <commands/decryptverifyclipboardcommand.h> -#include <commands/encryptclipboardcommand.h> #include <commands/importcertificatefromclipboardcommand.h> #include <commands/setinitialpincommand.h> -#include <commands/signclipboardcommand.h> +#include <commands/signencryptclipboardcommand.h> #include <KAboutApplicationDialog> #include <KAboutData> #include <KActionMenu> #include <KLocalizedString> #include <QEventLoopLocker> #include <QIcon> #include <QAction> #include <QApplication> #include <QMenu> #include <QPointer> #include <QSignalBlocker> using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; class SysTrayIcon::Private { friend class ::SysTrayIcon; SysTrayIcon *const q; public: explicit Private(SysTrayIcon *qq); ~Private(); private: void slotAbout() { if (!aboutDialog) { aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData()); aboutDialog->setAttribute(Qt::WA_DeleteOnClose); } if (aboutDialog->isVisible()) { aboutDialog->raise(); } else { aboutDialog->show(); } } void enableDisableActions() { openCertificateManagerAction.setEnabled(!q->mainWindow() || !q->mainWindow()->isVisible()); setInitialPinAction.setEnabled(!firstCardWithNullPin.empty()); q->setAttentionWanted(!firstCardWithNullPin.empty() && !q->attentionWindow()); } void slotSetInitialPin() { if (!firstCardWithNullPin.empty()) { auto cmd = new SetInitialPinCommand(firstCardWithNullPin); q->setAttentionWindow(cmd->dialog()); startCommand(cmd); } } void startCommand(Command *cmd) { Q_ASSERT(cmd); cmd->setParent(q->mainWindow()); cmd->start(); } private: std::string firstCardWithNullPin; QMenu menu; QAction openCertificateManagerAction; QAction configureAction; QAction aboutAction; QAction quitAction; ClipboardMenu clipboardMenu; QMenu cardMenu; QAction updateCardStatusAction; QAction setInitialPinAction; QPointer<KAboutApplicationDialog> aboutDialog; }; SysTrayIcon::Private::Private(SysTrayIcon *qq) : q(qq) , menu() , openCertificateManagerAction(i18nc("@action:inmenu", "&Open Certificate Manager..."), q) , configureAction(QIcon::fromTheme(QStringLiteral("configure")), xi18nc("@action:inmenu", "&Configure <application>%1</application>...", KAboutData::applicationData().displayName()), q) , aboutAction(QIcon::fromTheme(QStringLiteral("kleopatra")), xi18nc("@action:inmenu", "&About <application>%1</application>...", KAboutData::applicationData().displayName()), q) , quitAction(QIcon::fromTheme(QStringLiteral("application-exit")), xi18nc("@action:inmenu", "&Shutdown <application>%1</application>", KAboutData::applicationData().displayName()), q) , clipboardMenu(q) , cardMenu(i18nc("@title:menu", "SmartCard")) , updateCardStatusAction(i18nc("@action:inmenu", "Update Card Status"), q) , setInitialPinAction(i18nc("@action:inmenu", "Set NetKey v3 Initial PIN..."), q) , aboutDialog() { #ifdef Q_OS_WIN q->setNormalIcon(QIcon::fromTheme(QStringLiteral("kleopatra"))); #else q->setNormalIcon(QIcon::fromTheme(QStringLiteral("kleopatra-symbolic"))); #endif q->setAttentionIcon(QIcon::fromTheme(QStringLiteral("auth-sim-locked"))); Q_SET_OBJECT_NAME(menu); Q_SET_OBJECT_NAME(openCertificateManagerAction); Q_SET_OBJECT_NAME(configureAction); Q_SET_OBJECT_NAME(aboutAction); Q_SET_OBJECT_NAME(quitAction); Q_SET_OBJECT_NAME(clipboardMenu); Q_SET_OBJECT_NAME(cardMenu); Q_SET_OBJECT_NAME(setInitialPinAction); connect(&openCertificateManagerAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseMainWindow())); connect(&configureAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseConfigDialog())); connect(&aboutAction, SIGNAL(triggered()), q, SLOT(slotAbout())); connect(&quitAction, &QAction::triggered, QCoreApplication::instance(), &QCoreApplication::quit); connect(&updateCardStatusAction, &QAction::triggered, ReaderStatus::instance(), &ReaderStatus::updateStatus); connect(&setInitialPinAction, SIGNAL(triggered()), q, SLOT(slotSetInitialPin())); menu.addAction(&openCertificateManagerAction); menu.addAction(&configureAction); menu.addAction(&aboutAction); menu.addSeparator(); menu.addMenu(clipboardMenu.clipboardMenu()->menu()); menu.addSeparator(); menu.addMenu(&cardMenu); cardMenu.addAction(&updateCardStatusAction); cardMenu.addAction(&setInitialPinAction); menu.addSeparator(); menu.addAction(&quitAction); q->setContextMenu(&menu); clipboardMenu.setMainWindow(q->mainWindow()); } SysTrayIcon::Private::~Private() { } SysTrayIcon::SysTrayIcon(QObject *p) : SystemTrayIcon(p) , d(new Private(this)) { slotEnableDisableActions(); } SysTrayIcon::~SysTrayIcon() { } MainWindow *SysTrayIcon::mainWindow() const { return static_cast<MainWindow *>(SystemTrayIcon::mainWindow()); } QDialog *SysTrayIcon::attentionWindow() const { return static_cast<QDialog *>(SystemTrayIcon::attentionWindow()); } void SysTrayIcon::doActivated() { if (const QWidget *const aw = attentionWindow()) if (aw->isVisible()) { return; // ignore clicks while an attention window is open. } if (!d->firstCardWithNullPin.empty()) { d->slotSetInitialPin(); } else { // Toggle visibility of MainWindow KleopatraApplication::instance()->toggleMainWindowVisibility(); } } void SysTrayIcon::setFirstCardWithNullPin(const std::string &serialNumber) { if (d->firstCardWithNullPin == serialNumber) { return; } d->firstCardWithNullPin = serialNumber; slotEnableDisableActions(); } void SysTrayIcon::slotEnableDisableActions() { d->enableDisableActions(); } #include "moc_systrayicon.cpp" #endif // QT_NO_SYSTEMTRAYICON diff --git a/src/utils/clipboardmenu.cpp b/src/utils/clipboardmenu.cpp index f4a04ad4a..4bb842935 100644 --- a/src/utils/clipboardmenu.cpp +++ b/src/utils/clipboardmenu.cpp @@ -1,149 +1,115 @@ /* SPDX-FileCopyrightText: 2014-2021 Laurent Montel <montel@kde.org> SPDX-License-Identifier: GPL-2.0-only */ #include <config-kleopatra.h> #include "clipboardmenu.h" #include "kdtoolsglobal.h" #include "mainwindow.h" #include <settings.h> #include <commands/decryptverifyclipboardcommand.h> -#include <commands/encryptclipboardcommand.h> #include <commands/importcertificatefromclipboardcommand.h> -#include <commands/signclipboardcommand.h> +#include <commands/signencryptclipboardcommand.h> #include <Libkleo/Algorithm> #include <Libkleo/Compat> #include <Libkleo/KeyCache> #include <KActionMenu> #include <KLocalizedString> #include <QAction> #include <QApplication> #include <QClipboard> #include <QSignalBlocker> #include <gpgme++/key.h> using namespace Kleo; using namespace Kleo::Commands; ClipboardMenu::ClipboardMenu(QObject *parent) : QObject{parent} { mClipboardMenu = new KActionMenu(i18n("Clipboard"), this); mImportClipboardAction = new QAction(i18nc("@action", "Certificate Import"), this); - mEncryptClipboardAction = new QAction(i18nc("@action", "Encrypt..."), this); + mSignEncryptClipboardAction = new QAction(i18nc("@action", "Sign/Encrypt..."), this); const Kleo::Settings settings{}; - if (settings.cmsEnabled() && settings.cmsSigningAllowed()) { - mSmimeSignClipboardAction = new QAction(i18nc("@action", "S/MIME-Sign..."), this); - Q_SET_OBJECT_NAME(mSmimeSignClipboardAction); - } - mOpenPGPSignClipboardAction = new QAction(i18nc("@action", "OpenPGP-Sign..."), this); mDecryptVerifyClipboardAction = new QAction(i18nc("@action", "Decrypt/Verify..."), this); Q_SET_OBJECT_NAME(mClipboardMenu); Q_SET_OBJECT_NAME(mImportClipboardAction); - Q_SET_OBJECT_NAME(mEncryptClipboardAction); - Q_SET_OBJECT_NAME(mOpenPGPSignClipboardAction); Q_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(mSignEncryptClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotSignEncryptClipboard); connect(mDecryptVerifyClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotDecryptVerifyClipboard); mClipboardMenu->addAction(mImportClipboardAction); - mClipboardMenu->addAction(mEncryptClipboardAction); - if (mSmimeSignClipboardAction) { - mClipboardMenu->addAction(mSmimeSignClipboardAction); - } - mClipboardMenu->addAction(mOpenPGPSignClipboardAction); + mClipboardMenu->addAction(mSignEncryptClipboardAction); mClipboardMenu->addAction(mDecryptVerifyClipboardAction); - connect(QApplication::clipboard(), &QClipboard::changed, this, &ClipboardMenu::slotEnableDisableActions); + connect(QApplication::clipboard(), &QClipboard::dataChanged, 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() +void ClipboardMenu::slotSignEncryptClipboard() { - startCommand(new SignClipboardCommand(GpgME::CMS, nullptr)); + startCommand(new SignEncryptClipboardCommand(nullptr)); } void ClipboardMenu::slotDecryptVerifyClipboard() { startCommand(new DecryptVerifyClipboardCommand(nullptr)); } -namespace -{ - -bool hasSigningKeys(GpgME::Protocol protocol) -{ - if (!KeyCache::instance()->initialized()) { - return false; - } - return std::ranges::any_of(KeyCache::instance()->keys(), [protocol](const auto &k) { - return k.hasSecret() && Kleo::keyHasSign(k) && (k.protocol() == protocol); - }); -} - -} - 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)); +#ifdef Q_OS_UNIX + // We can't reliable monitor the clipboard on wayland when the app doesn't have focus, so we always enable the actions there + // and show an error when an action is done on invalid data + if (qApp->platformName() == QStringLiteral("wayland")) { + mImportClipboardAction->setEnabled(true); + mSignEncryptClipboardAction->setEnabled(true); + mDecryptVerifyClipboardAction->setEnabled(true); + } else +#endif + { + mImportClipboardAction->setEnabled(ImportCertificateFromClipboardCommand::canImportCurrentClipboard()); + mSignEncryptClipboardAction->setEnabled(SignEncryptClipboardCommand::canSignEncryptCurrentClipboard()); + mDecryptVerifyClipboardAction->setEnabled(DecryptVerifyClipboardCommand::canDecryptVerifyCurrentClipboard()); } - mDecryptVerifyClipboardAction->setEnabled(DecryptVerifyClipboardCommand::canDecryptVerifyCurrentClipboard()); } #include "moc_clipboardmenu.cpp" diff --git a/src/utils/clipboardmenu.h b/src/utils/clipboardmenu.h index 1e0cfd51f..b8a7f1097 100644 --- a/src/utils/clipboardmenu.h +++ b/src/utils/clipboardmenu.h @@ -1,50 +1,46 @@ /* SPDX-FileCopyrightText: 2014-2021 Laurent Montel <montel@kde.org> SPDX-License-Identifier: GPL-2.0-only */ #pragma once #include <QObject> #include <QPointer> class KActionMenu; class QAction; class MainWindow; namespace Kleo { class Command; } class ClipboardMenu : public QObject { Q_OBJECT public: explicit ClipboardMenu(QObject *parent = nullptr); ~ClipboardMenu() override; void setMainWindow(MainWindow *window); KActionMenu *clipboardMenu() const; private Q_SLOTS: void slotImportClipboard(); - void slotEncryptClipboard(); - void slotOpenPGPSignClipboard(); - void slotSMIMESignClipboard(); + void slotSignEncryptClipboard(); void slotDecryptVerifyClipboard(); void slotEnableDisableActions(); private: void startCommand(Kleo::Command *cmd); QPointer<KActionMenu> mClipboardMenu; QPointer<QAction> mImportClipboardAction; - QPointer<QAction> mEncryptClipboardAction; - QPointer<QAction> mSmimeSignClipboardAction; - QPointer<QAction> mOpenPGPSignClipboardAction; + QPointer<QAction> mSignEncryptClipboardAction; QPointer<QAction> mDecryptVerifyClipboardAction; QPointer<MainWindow> mWindow; };