Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6220bcf3d..cb6b83f62 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,637 +1,639 @@
# 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
selftest/registrycheck.cpp selftest/registrycheck.h
utils/gnupg-registry.c
utils/userinfo_win.cpp
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/addsubkeycommand.cpp
commands/addsubkeycommand.h
commands/adduseridcommand.cpp
commands/adduseridcommand.h
commands/authenticatepivcardapplicationcommand.cpp
commands/authenticatepivcardapplicationcommand.h
commands/cardcommand.cpp
commands/cardcommand.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/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/signencryptfilescommand.cpp
commands/signencryptfilescommand.h
commands/signencryptfoldercommand.cpp
commands/signencryptfoldercommand.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/signencryptfileswizard.cpp
crypto/gui/signencryptfileswizard.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/cardinfotab.cpp
dialogs/cardinfotab.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/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/nameandemailwidget.cpp
dialogs/nameandemailwidget.h
dialogs/newopenpgpcertificatedetailsdialog.cpp
dialogs/newopenpgpcertificatedetailsdialog.h
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.h
dialogs/revokekeydialog.cpp
dialogs/revokekeydialog.h
dialogs/selftestdialog.cpp
dialogs/selftestdialog.h
dialogs/setinitialpindialog.cpp
dialogs/setinitialpindialog.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/keyalgo.cpp
newcertificatewizard/keyalgo_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/expiration.cpp
utils/expiration.h
utils/filedialog.cpp
utils/filedialog.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/keyparameters.cpp
utils/keyparameters.h
utils/kuniqueservice.cpp
utils/kuniqueservice.h
utils/log.cpp
utils/log.h
utils/memory-helpers.h
utils/migration.cpp
utils/migration.h
utils/multivalidator.cpp
utils/multivalidator.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/systemtrayicon.cpp
utils/systemtrayicon.h
utils/tags.cpp
utils/tags.h
utils/types.cpp
utils/types.h
utils/userinfo.cpp
utils/userinfo.h
utils/validation.cpp
utils/validation.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/errorlabel.cpp
view/errorlabel.h
view/formtextinput.cpp
view/formtextinput.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/nullpinwidget.cpp
view/nullpinwidget.h
view/openpgpkeycardwidget.cpp
view/openpgpkeycardwidget.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/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
)
if(WIN32)
configure_file (versioninfo.rc.in versioninfo.rc)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS})
configure_file (kleopatra.w32-manifest.in kleopatra.w32-manifest)
set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/kleopatra.w32-manifest ${_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/tagspreferences.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
)
diff --git a/src/commands/cardcommand.cpp b/src/commands/cardcommand.cpp
index da234ef94..d31894ea0 100644
--- a/src/commands/cardcommand.cpp
+++ b/src/commands/cardcommand.cpp
@@ -1,76 +1,76 @@
/* commands/cardcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "cardcommand.h"
#include "cardcommand_p.h"
#include <smartcard/readerstatus.h>
#include <QWidget>
using namespace Kleo;
CardCommand::Private *CardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CardCommand::Private *CardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CardCommand::Private::Private(CardCommand *qq, const std::string &serialNumber, QWidget *parent)
: Command::Private(qq, parent)
, serialNumber_(serialNumber)
{
}
CardCommand::Private::~Private()
{
}
void CardCommand::Private::doFinish()
{
if (autoResetCardToOpenPGP) {
SmartCard::ReaderStatus::switchCardBackToOpenPGPApp(serialNumber());
}
}
CardCommand::CardCommand(const std::string &serialNumber, QWidget *parent)
: Command(new Private(this, serialNumber, parent))
{
}
-CardCommand::CardCommand(Private *pp)
- : Command(pp)
+CardCommand::CardCommand(Private *pp, QAbstractItemView *view)
+ : Command(view, pp)
{
}
CardCommand::~CardCommand()
{
}
void CardCommand::setAutoResetCardToOpenPGP(bool autoReset)
{
d->autoResetCardToOpenPGP = autoReset;
}
bool CardCommand::autoResetCardToOpenPGP() const
{
return d->autoResetCardToOpenPGP;
}
#undef q_func
#undef d_func
#include "moc_cardcommand.cpp"
diff --git a/src/commands/cardcommand.h b/src/commands/cardcommand.h
index d854c57d8..81195e8c9 100644
--- a/src/commands/cardcommand.h
+++ b/src/commands/cardcommand.h
@@ -1,37 +1,37 @@
/* commands/cardcommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "command.h"
namespace Kleo
{
class CardCommand : public Command
{
Q_OBJECT
public:
explicit CardCommand(const std::string &serialNumber, QWidget *parent);
~CardCommand() override;
void setAutoResetCardToOpenPGP(bool autoReset);
bool autoResetCardToOpenPGP() const;
protected:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
protected:
- explicit CardCommand(Private *pp);
+ explicit CardCommand(Private *pp, QAbstractItemView *view = nullptr);
};
} // namespace Kleo
diff --git a/src/commands/command.h b/src/commands/command.h
index 830d71632..fca0e8f62 100644
--- a/src/commands/command.h
+++ b/src/commands/command.h
@@ -1,140 +1,141 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/command.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QObject>
#include <qwindowdefs.h> // for WId
#include <utils/types.h> // for ExecutionContext
#include <memory>
#include <vector>
class QModelIndex;
template<typename T>
class QList;
class QAbstractItemView;
namespace GpgME
{
class Key;
}
namespace Kleo
{
class KeyListController;
class AbstractKeyListSortFilterProxyModel;
class Command : public QObject, public ExecutionContext
{
Q_OBJECT
public:
explicit Command(KeyListController *parent);
explicit Command(QAbstractItemView *view, KeyListController *parent);
explicit Command(const GpgME::Key &key);
explicit Command(const std::vector<GpgME::Key> &keys);
~Command() override;
enum Restriction {
// clang-format off
NoRestriction = 0x0000,
NeedSelection = 0x0001,
OnlyOneKey = 0x0002,
NeedSecretKey = 0x0004, //< command performs secret key operations
NeedSecretPrimaryKeyData = 0x0008, //< command needs access to the secret key data of the primary key
NeedSecretSubkeyData = 0x0010, //< command needs access to the secret key data of one or more subkeys
// esoteric:
MayOnlyBeSecretKeyIfOwnerTrustIsNotYetUltimate = 0x0040, // for set-owner-trust
AnyCardHasNullPin = 0x0080,
+ SuitableForCard = 0x0100,
MustBeRoot = 0x0200,
MustBeTrustedRoot = 0x0400 | MustBeRoot,
MustBeUntrustedRoot = 0x0800 | MustBeRoot,
MustBeValid = 0x1000, //< key is neither revoked nor expired nor otherwise "bad"
MustBeOpenPGP = 0x2000,
MustBeCMS = 0x4000,
_AllRestrictions_Helper,
AllRestrictions = 2 * (_AllRestrictions_Helper - 1) - 1,
// clang-format on
};
Q_DECLARE_FLAGS(Restrictions, Restriction)
static Restrictions restrictions()
{
return NoRestriction;
}
/** Classify the files and return the most appropriate commands.
*
* @param files: A list of files.
*
* @returns null QString on success. Error message otherwise.
*/
static QList<Command *> commandsForFiles(const QStringList &files, KeyListController *controller);
/** Get a command for a query.
*
* @param query: A keyid / fingerprint or any string to use in the search.
*/
static Command *commandForQuery(const QString &query);
void setParentWidget(QWidget *widget);
void setParentWId(WId wid);
void setView(QAbstractItemView *view);
void setKey(const GpgME::Key &key);
void setKeys(const std::vector<GpgME::Key> &keys);
void setAutoDelete(bool on);
bool autoDelete() const;
void setWarnWhenRunningAtShutdown(bool warn);
bool warnWhenRunningAtShutdown() const;
public Q_SLOTS:
void start();
void cancel();
Q_SIGNALS:
void info(const QString &message, int timeout = 0);
void progress(int current, int total);
void finished();
void canceled();
private:
virtual void doStart() = 0;
virtual void doCancel() = 0;
private:
void applyWindowID(QWidget *wid) const override;
protected:
void addTemporaryView(const QString &title, AbstractKeyListSortFilterProxyModel *proxy = nullptr, const QString &tabToolTip = QString());
protected:
class Private;
const std::unique_ptr<Private> d;
protected:
explicit Command(Private *pp);
explicit Command(QAbstractItemView *view, Private *pp);
explicit Command(const std::vector<GpgME::Key> &keys, Private *pp);
explicit Command(const GpgME::Key &key, Private *pp);
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(Kleo::Command::Restrictions)
diff --git a/src/commands/exportpaperkeycommand.cpp b/src/commands/exportpaperkeycommand.cpp
index 53cf6b3ae..f4450bad5 100644
--- a/src/commands/exportpaperkeycommand.cpp
+++ b/src/commands/exportpaperkeycommand.cpp
@@ -1,182 +1,187 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportpaperkeycommand.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 <config-kleopatra.h>
#include "exportpaperkeycommand.h"
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <QGpgME/ExportJob>
#include <QGpgME/Protocol>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <KMessageBox>
#include <QFontDatabase>
#include <QPrintDialog>
#include <QPrinter>
#include <QTextDocument>
#include "command_p.h"
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
class ExportPaperKeyCommand::Private : public Command::Private
{
friend class ::ExportPaperKeyCommand;
ExportPaperKeyCommand *q_func() const
{
return static_cast<ExportPaperKeyCommand *>(q);
}
public:
- explicit Private(ExportPaperKeyCommand *qq, KeyListController *c);
+ explicit Private(ExportPaperKeyCommand *qq, KeyListController *c = nullptr);
~Private() override;
void startPaperKey(const QByteArray &data);
private:
QProcess pkProc;
QPointer<QGpgME::ExportJob> job;
};
ExportPaperKeyCommand::Private::Private(ExportPaperKeyCommand *qq, KeyListController *c)
: Command::Private(qq, c)
{
}
ExportPaperKeyCommand::Private::~Private()
{
}
ExportPaperKeyCommand::Private *ExportPaperKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ExportPaperKeyCommand::Private *ExportPaperKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ExportPaperKeyCommand::ExportPaperKeyCommand(QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
}
+ExportPaperKeyCommand::ExportPaperKeyCommand(const GpgME::Key &key)
+ : Command(key, new Private(this))
+{
+}
+
void ExportPaperKeyCommand::doStart()
{
if (paperKeyInstallPath().isNull()) {
KMessageBox::error(d->parentWidgetOrView(),
xi18nc("@info",
"<para><application>Kleopatra</application> uses "
"<application>PaperKey</application> to create a minimized and"
" printable version of your secret key.</para>"
"<para>Please make sure it is installed.</para>"),
i18nc("@title", "Failed to find PaperKey executable."));
finished();
return;
}
const auto key = d->key();
if (key.isNull()) {
finished();
return;
}
std::unique_ptr<QGpgME::ExportJob> exportJob{QGpgME::openpgp()->secretKeyExportJob(false)};
connect(exportJob.get(), &QGpgME::ExportJob::result, this, [this](const GpgME::Error &err, const QByteArray &keyData) {
if (err.isCanceled()) {
finished();
return;
}
if (err) {
d->error(xi18nc("@info",
"<para>An error occurred during export of the secret key:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)));
finished();
return;
}
d->startPaperKey(keyData);
});
const GpgME::Error err = exportJob->start({QLatin1StringView{key.primaryFingerprint()}});
if (err) {
d->error(xi18nc("@info",
"<para>An error occurred during export of the secret key:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)));
finished();
return;
}
d->job = exportJob.release();
}
void ExportPaperKeyCommand::Private::startPaperKey(const QByteArray &data)
{
pkProc.setProgram(paperKeyInstallPath());
pkProc.setArguments({QStringLiteral("--output-type=base16")});
qCDebug(KLEOPATRA_LOG) << "Starting PaperKey process.";
pkProc.start();
pkProc.write(data);
pkProc.closeWriteChannel();
pkProc.waitForFinished();
qCDebug(KLEOPATRA_LOG) << "Paperkey export finished: " << pkProc.exitCode() << "status: " << pkProc.exitStatus();
if (pkProc.exitStatus() == QProcess::CrashExit || pkProc.exitCode()) {
error(xi18nc("@info",
"<para><application>PaperKey</application> failed with error</para>"
"<para><message>%1</message></para>",
pkProc.errorString()));
finished();
return;
}
QPrinter printer;
const auto key = this->key();
printer.setDocName(QStringLiteral("0x%1-sec").arg(QString::fromLatin1(key.shortKeyID())));
QPrintDialog printDialog(&printer, parentWidgetOrView());
printDialog.setWindowTitle(i18nc("@title:window", "Print Secret Key"));
if (printDialog.exec() != QDialog::Accepted) {
qCDebug(KLEOPATRA_LOG) << "Printing aborted.";
finished();
return;
}
QTextDocument doc(QString::fromLatin1(pkProc.readAllStandardOutput()));
doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
doc.print(&printer);
finished();
}
void ExportPaperKeyCommand::doCancel()
{
if (d->job) {
d->job->slotCancel();
}
d->job.clear();
}
#include "moc_exportpaperkeycommand.cpp"
diff --git a/src/commands/exportpaperkeycommand.h b/src/commands/exportpaperkeycommand.h
index fdf324cdb..30df12e04 100644
--- a/src/commands/exportpaperkeycommand.h
+++ b/src/commands/exportpaperkeycommand.h
@@ -1,46 +1,47 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportpaperkeycommand.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 <commands/command.h>
#include <QProcess>
#include <QString>
class QWidget;
namespace Kleo
{
namespace Commands
{
class ExportPaperKeyCommand : public Command
{
Q_OBJECT
public:
explicit ExportPaperKeyCommand(QAbstractItemView *view, KeyListController *parent);
+ explicit ExportPaperKeyCommand(const GpgME::Key &key);
static Restrictions restrictions()
{
return OnlyOneKey | NeedSecretPrimaryKeyData | MustBeOpenPGP;
}
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
void doStart() override;
void doCancel() override;
};
}
}
diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp
index 66b4005c2..f7ab5162a 100644
--- a/src/commands/keytocardcommand.cpp
+++ b/src/commands/keytocardcommand.cpp
@@ -1,782 +1,983 @@
/* commands/keytocardcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020,2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keytocardcommand.h"
#include "cardcommand_p.h"
#include "authenticatepivcardapplicationcommand.h"
+#include "commands/exportpaperkeycommand.h"
+#include "dialogs/copytosmartcarddialog.h"
#include "smartcard/algorithminfo.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include <utils/applicationstate.h>
#include <utils/filedialog.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KeySelectionDialog>
+#include <QGpgME/ExportJob>
+#include <gpgme.h>
+
+#include <KFileUtils>
#include <KLocalizedString>
#include <QDateTime>
#include <QDir>
#include <QInputDialog>
#include <QSaveFile>
+#include <QStandardPaths>
#include <QStringList>
#include <gpg-error.h>
#if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
#define GPG_ERROR_HAS_NO_AUTH
#endif
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
+namespace
+{
+struct GripAndSlot {
+ std::string keygrip;
+ std::string slot;
+};
+}
+
namespace
{
QString cardDisplayName(const std::shared_ptr<const Card> &card)
{
return i18nc("smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber());
}
}
class KeyToCardCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::KeyToCardCommand;
KeyToCardCommand *q_func() const
{
return static_cast<KeyToCardCommand *>(q);
}
public:
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey);
+ explicit Private(KeyToCardCommand *qq);
explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName);
private:
enum Confirmation {
AskForConfirmation,
SkipConfirmation,
};
void start();
void startKeyToOpenPGPCard();
Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card);
void startKeyToPIVCard();
void authenticate();
void authenticationFinished();
void authenticationCanceled();
void keyToCardDone(const GpgME::Error &err);
void keyToPIVCardDone(const GpgME::Error &err);
void updateDone();
void keyHasBeenCopiedToCard();
void backupHasBeenCreated(const QString &backupFilename);
QString backupKey();
std::vector<QByteArray> readSecretKeyFile();
bool writeSecretKeyBackup(const QString &filename, const std::vector<QByteArray> &keydata);
void startDeleteSecretKeyLocally(Confirmation confirmation);
void deleteSecretKeyLocallyFinished(const GpgME::Error &err);
+ void copyKey();
+ void nextSubkey();
+ void deleteNextKey();
+
private:
+ std::vector<GripAndSlot> gripsAndSlots;
std::string appName;
- GpgME::Subkey subkey;
+ std::vector<GpgME::Subkey> subkeys;
+ std::vector<GpgME::Subkey> remainingSubkeys;
std::string cardSlot;
bool overwriteExistingAlreadyApproved = false;
bool hasBeenCanceled = false;
QMetaObject::Connection updateConnection;
+ bool removeSecretKey = false;
+ QString exportPath;
};
KeyToCardCommand::Private *KeyToCardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const KeyToCardCommand::Private *KeyToCardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey_)
: CardCommand::Private(qq, "", nullptr)
- , subkey(subkey_)
+ , subkeys(std::vector{subkey_})
+{
+}
+
+KeyToCardCommand::Private::Private(KeyToCardCommand *qq)
+ : CardCommand::Private(qq, "", nullptr)
{
}
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName_)
: CardCommand::Private(qq, serialNumber, nullptr)
, appName(appName_)
, cardSlot(slot)
{
}
namespace
{
+static std::shared_ptr<Card> getEmptyCard(const Key &key)
+{
+ const std::vector<std::shared_ptr<Card>> suitableCards = KeyToCardCommand::getSuitableCards(key);
+ for (const auto &card : suitableCards) {
+ if (card->keyFingerprint(card->signingKeyRef()).empty() && card->keyFingerprint(card->encryptionKeyRef()).empty()
+ && card->keyFingerprint(card->authenticationKeyRef()).empty()) {
+ return card;
+ }
+ }
+ return std::shared_ptr<Card>();
+}
+
static std::shared_ptr<Card> getCardToTransferSubkeyTo(const Subkey &subkey, QWidget *parent)
{
const std::vector<std::shared_ptr<Card>> suitableCards = KeyToCardCommand::getSuitableCards(subkey);
if (suitableCards.empty()) {
return std::shared_ptr<Card>();
} else if (suitableCards.size() == 1) {
return suitableCards[0];
}
QStringList options;
for (const auto &card : suitableCards) {
options.push_back(cardDisplayName(card));
}
bool ok;
const QString choice = QInputDialog::getItem(parent,
i18n("Select Card"),
i18n("Please select the card the key should be written to:"),
options,
/* current= */ 0,
/* editable= */ false,
&ok);
if (!ok) {
return std::shared_ptr<Card>();
}
const int index = options.indexOf(choice);
return suitableCards[index];
}
}
void KeyToCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()";
- if (!subkey.isNull() && serialNumber().empty()) {
- const auto card = getCardToTransferSubkeyTo(subkey, parentWidgetOrView());
+ if (!key().isNull()) {
+ const auto card = getEmptyCard(key());
if (!card) {
+ error(i18nc("@info", "No empty card was found."));
finished();
return;
}
+ auto dialog = new Dialogs::CopyToSmartcardDialog(parentWidgetOrView());
+ dialog->setKey(key());
+ dialog->setCardDisplayName(cardDisplayName(card));
+ dialog->exec();
+
setSerialNumber(card->serialNumber());
appName = card->appName();
- }
- const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
- if (!card) {
- error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
- finished();
- return;
- }
+ auto choice = dialog->backupChoice();
- if (card->appName() == SmartCard::OpenPGPCard::AppName) {
- startKeyToOpenPGPCard();
- } else if (card->appName() == SmartCard::PIVCard::AppName) {
- startKeyToPIVCard();
- } else {
- error(xi18nc("@info", "Sorry! Writing keys to the card <emphasis>%1</emphasis> is not supported.", cardDisplayName(card)));
- finished();
+ if (choice != Dialogs::CopyToSmartcardDialog::KeepKey) {
+ removeSecretKey = true;
+ }
+
+ if (choice == Dialogs::CopyToSmartcardDialog::FileBackup) {
+ auto job = QGpgME::openpgp()->publicKeyExportJob(true);
+ job->setExportFlags(GPGME_EXPORT_MODE_MINIMAL);
+ job->start({QString::fromLatin1(key().primaryFingerprint())});
+ connect(job, &QGpgME::ExportJob::result, q, [this](const auto &error, const auto &data) {
+ if (error.isCanceled()) {
+ finished();
+ return;
+ }
+
+ if (error) {
+ this->error(i18nc("@info", "Failed to backup key: %1", Formatting::errorAsString(error)));
+ finished();
+ return;
+ }
+
+ auto name = Formatting::prettyName(key());
+ if (name.isEmpty()) {
+ name = Formatting::prettyEMail(key());
+ }
+
+ auto filename = QStringLiteral("%1_%2_public.asc").arg(name, Formatting::prettyKeyID(key().shortKeyID()));
+ const auto dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ if (QFileInfo::exists(QStringLiteral("%1/%2").arg(dir, filename))) {
+ filename = KFileUtils::suggestName(QUrl::fromLocalFile(dir), filename);
+ }
+ exportPath = QStringLiteral("%1/%2").arg(dir, filename);
+ QFile file(exportPath);
+ file.open(QIODevice::WriteOnly);
+ file.write(data);
+ file.close();
+
+ copyKey();
+ });
+ } else if (choice == Dialogs::CopyToSmartcardDialog::PrintBackup) {
+ auto exportPaperKey = new ExportPaperKeyCommand(key());
+ exportPaperKey->start();
+ connect(exportPaperKey, &ExportPaperKeyCommand::finished, q, [this]() {
+ copyKey();
+ });
+ } else {
+ copyKey();
+ }
return;
+ } else if (!subkeys.empty() && !subkeys[0].isNull() && serialNumber().empty()) {
+ const auto card = getCardToTransferSubkeyTo(subkeys[0], parentWidgetOrView());
+ if (!card) {
+ finished();
+ return;
+ }
+ setSerialNumber(card->serialNumber());
+ appName = card->appName();
}
+
+ copyKey();
}
namespace
{
static std::string getOpenPGPCardSlotForKey(const GpgME::Subkey &subKey, QWidget *parent)
{
// Check if we need to ask the user for the slot
if ((subKey.canSign() || subKey.canCertify()) && !subKey.canEncrypt() && !subKey.canAuthenticate()) {
// Signing only
return OpenPGPCard::pgpSigKeyRef();
}
if (subKey.canEncrypt() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canAuthenticate()) {
// Encrypt only
return OpenPGPCard::pgpEncKeyRef();
}
if (subKey.canAuthenticate() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canEncrypt()) {
// Auth only
return OpenPGPCard::pgpAuthKeyRef();
}
// Multiple uses, ask user.
QStringList options;
std::vector<std::string> cardSlots;
if (subKey.canSign() || subKey.canCertify()) {
options.push_back(i18nc("@item:inlistbox", "Signature"));
cardSlots.push_back(OpenPGPCard::pgpSigKeyRef());
}
if (subKey.canEncrypt()) {
options.push_back(i18nc("@item:inlistbox", "Encryption"));
cardSlots.push_back(OpenPGPCard::pgpEncKeyRef());
}
if (subKey.canAuthenticate()) {
options.push_back(i18nc("@item:inlistbox", "Authentication"));
cardSlots.push_back(OpenPGPCard::pgpAuthKeyRef());
}
bool ok;
const QString choice = QInputDialog::getItem(parent,
i18n("Select Card Slot"),
i18n("Please select the card slot the key should be written to:"),
options,
/* current= */ 0,
/* editable= */ false,
&ok);
const int choiceIndex = options.indexOf(choice);
if (ok && choiceIndex >= 0) {
return cardSlots[choiceIndex];
} else {
return {};
}
}
}
void KeyToCardCommand::Private::startKeyToOpenPGPCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToOpenPGPCard()";
- const auto pgpCard = SmartCard::ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
- if (!pgpCard) {
- error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber())));
- finished();
- return;
- }
-
- if (subkey.isNull()) {
+ if ((subkeys.empty() || subkeys[0].isNull()) && key().isNull()) {
finished();
return;
}
- if (subkey.parent().protocol() != GpgME::OpenPGP) {
+ if ((!subkeys.empty() && !subkeys[0].isNull() && subkeys[0].parent().protocol() != GpgME::OpenPGP)
+ || (!key().isNull() && key().protocol() != GpgME::OpenPGP)) {
error(i18n("Sorry! This key cannot be transferred to an OpenPGP card."));
finished();
return;
}
- cardSlot = getOpenPGPCardSlotForKey(subkey, parentWidgetOrView());
- if (cardSlot.empty()) {
- finished();
- return;
+ if (!key().isNull()) {
+ subkeys = key().subkeys();
}
- // Check if we need to do the overwrite warning.
- const std::string existingKey = pgpCard->keyFingerprint(cardSlot);
- if (!existingKey.empty()) {
- const auto encKeyWarning = (cardSlot == OpenPGPCard::pgpEncKeyRef())
- ? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
- : QString{};
- const QString message = i18nc("@info",
- "<p>The card <em>%1</em> already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
- "<p>If there is no backup the existing key will be irrecoverably lost.</p>",
- cardDisplayName(pgpCard))
- + i18n("The existing key has the fingerprint:") + QStringLiteral("<pre>%1</pre>").arg(Formatting::prettyID(existingKey.c_str())) + encKeyWarning;
- const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(),
- message,
- i18nc("@title:window", "Overwrite existing key"),
- KGuiItem{i18nc("@action:button", "Overwrite Existing Key")},
- KStandardGuiItem::cancel(),
- QString(),
- KMessageBox::Notify | KMessageBox::Dangerous);
- if (choice != KMessageBox::Continue) {
- finished();
- return;
- }
- }
+ remainingSubkeys = subkeys;
- // Now do the deed
- const auto time = QDateTime::fromSecsSinceEpoch(quint32(subkey.creationTime()), QTimeZone::utc());
- const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss"));
- const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 %3 %4")
- .arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()), QString::fromStdString(cardSlot), timestamp);
- ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, cmd.toUtf8(), q_func(), [this](const GpgME::Error &err) {
- keyToCardDone(err);
- });
+ nextSubkey();
}
namespace
{
static std::vector<Key> getSigningCertificates()
{
std::vector<Key> signingCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(signingCertificates.begin(), signingCertificates.end(), [](const Key &key) {
return !(key.protocol() == GpgME::CMS && !key.subkey(0).isNull() && key.subkey(0).canSign() && !key.subkey(0).canEncrypt() && key.subkey(0).isSecret()
&& !key.subkey(0).isCardKey());
});
signingCertificates.erase(it, signingCertificates.end());
return signingCertificates;
}
static std::vector<Key> getEncryptionCertificates()
{
std::vector<Key> encryptionCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(encryptionCertificates.begin(), encryptionCertificates.end(), [](const Key &key) {
return !(key.protocol() == GpgME::CMS && !key.subkey(0).isNull() && key.subkey(0).canEncrypt() && key.subkey(0).isSecret()
&& !key.subkey(0).isCardKey());
});
encryptionCertificates.erase(it, encryptionCertificates.end());
return encryptionCertificates;
}
}
Subkey KeyToCardCommand::Private::getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> & /*card*/)
{
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
return Subkey();
}
const std::vector<Key> certificates = cardSlot == PIVCard::cardAuthenticationKeyRef() ? getSigningCertificates() : getEncryptionCertificates();
if (certificates.empty()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
return Subkey();
}
auto dialog = new KeySelectionDialog(parentWidgetOrView());
dialog->setWindowTitle(i18nc("@title:window", "Select Certificate"));
dialog->setText(i18n("Please select the certificate whose key pair you want to write to the card:"));
dialog->setKeys(certificates);
if (dialog->exec() == QDialog::Rejected) {
return Subkey();
}
return dialog->selectedKey().subkey(0);
}
void KeyToCardCommand::Private::startKeyToPIVCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToPIVCard()";
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
// key to card is only supported for the Card Authentication key and the Key Management key
finished();
return;
}
- if (subkey.isNull()) {
- subkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard);
+ if (!subkeys.empty() && subkeys[0].isNull()) {
+ subkeys.push_back(getSubkeyToTransferToPIVCard(cardSlot, pivCard));
}
- if (subkey.isNull()) {
+ if (!subkeys.empty() && subkeys[0].isNull()) {
finished();
return;
}
- if (subkey.parent().protocol() != GpgME::CMS) {
+ if (subkeys[0].parent().protocol() != GpgME::CMS) {
error(i18n("Sorry! This key cannot be transferred to a PIV card."));
finished();
return;
}
- if (!subkey.canEncrypt() && !subkey.canSign()) {
+ if (!subkeys[0].canEncrypt() && !subkeys[0].canSign()) {
error(i18n("Sorry! Only encryption keys and signing keys can be transferred to a PIV card."));
finished();
return;
}
// Check if we need to do the overwrite warning.
if (!overwriteExistingAlreadyApproved) {
const std::string existingKey = pivCard->keyInfo(cardSlot).grip;
- if (!existingKey.empty() && (existingKey != subkey.keyGrip())) {
+ if (!existingKey.empty() && (existingKey != subkeys[0].keyGrip())) {
const QString decryptionWarning = (cardSlot == PIVCard::keyManagementKeyRef())
? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
: QString();
const QString message = i18nc("@info",
"<p>The card <em>%1</em> already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
"<p>If there is no backup the existing key will be irrecoverably lost.</p>",
cardDisplayName(pivCard))
+ i18n("The existing key has the key grip:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) + decryptionWarning;
const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(),
message,
i18nc("@title:window", "Overwrite existing key"),
KGuiItem{i18nc("@action:button", "Overwrite Existing Key")},
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
finished();
return;
}
overwriteExistingAlreadyApproved = true;
}
}
const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 %3")
- .arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()))
+ .arg(QString::fromLatin1(subkeys[0].keyGrip()), QString::fromStdString(serialNumber()))
.arg(QString::fromStdString(cardSlot));
ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, cmd.toUtf8(), q_func(), [this](const GpgME::Error &err) {
keyToPIVCardDone(err);
});
}
void KeyToCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
cmd->setAutoResetCardToOpenPGP(false);
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() {
authenticationFinished();
});
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() {
authenticationCanceled();
});
cmd->start();
}
void KeyToCardCommand::Private::authenticationFinished()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticationFinished()";
if (!hasBeenCanceled) {
startKeyToPIVCard();
}
}
void KeyToCardCommand::Private::authenticationCanceled()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticationCanceled()";
hasBeenCanceled = true;
canceled();
}
void KeyToCardCommand::Private::updateDone()
{
disconnect(updateConnection);
const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
if (!card) {
error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
- const std::string keyGripOnCard = card->keyInfo(cardSlot).grip;
- if (keyGripOnCard != subkey.keyGrip()) {
- qCWarning(KLEOPATRA_LOG) << q << __func__ << "KEYTOCARD succeeded, but key on card doesn't match copied key";
- error(i18nc("@info", "Copying the key to the card failed."));
- finished();
- return;
+ for (const auto &gripAndSlot : gripsAndSlots) {
+ const std::string keyGripOnCard = card->keyInfo(gripAndSlot.slot).grip;
+ if (keyGripOnCard != gripAndSlot.keygrip) {
+ qCWarning(KLEOPATRA_LOG) << q << __func__ << "KEYTOCARD succeeded, but key on card doesn't match copied key";
+ error(i18nc("@info", "Copying the key to the card failed."));
+ finished();
+ return;
+ }
}
+
keyHasBeenCopiedToCard();
}
void KeyToCardCommand::Private::keyHasBeenCopiedToCard()
{
+ // TODO decide on backup before or after copying
+ if (!key().isNull()) {
+ if (removeSecretKey) {
+ startDeleteSecretKeyLocally(AskForConfirmation);
+ }
+
+ if (exportPath.isEmpty()) {
+ information(xi18nc("@info", "<para>The key was copied to the smartcard.</para>"));
+ } else {
+ information(
+ xi18nc("@info", "<para>The key was copied to the smartcard.</para><para>A backup was exported to <filename>%1</filename></para>", exportPath));
+ }
+ return;
+ }
const auto answer = KMessageBox::questionTwoActionsCancel(parentWidgetOrView(),
xi18nc("@info",
"<para>The key has been copied to the card.</para>"
"<para>You can now delete the copy of the key stored on this computer. "
"Optionally, you can first create a backup of the key.</para>"),
i18nc("@title:window", "Success"),
KGuiItem{i18nc("@action:button", "Create backup")},
KGuiItem{i18nc("@action:button", "Delete copy on disk")},
KGuiItem{i18nc("@action:button", "Keep copy on disk")});
if (answer == KMessageBox::ButtonCode::PrimaryAction) {
const QString backupFilename = backupKey();
if (backupFilename.isEmpty()) {
// user canceled the backup or there was an error; repeat above question
QMetaObject::invokeMethod(q, [this]() {
keyHasBeenCopiedToCard();
});
}
backupHasBeenCreated(backupFilename);
} else if (answer == KMessageBox::ButtonCode::SecondaryAction) {
startDeleteSecretKeyLocally(AskForConfirmation);
} else {
finished();
}
}
void KeyToCardCommand::Private::backupHasBeenCreated(const QString &backupFilename)
{
const auto answer =
KMessageBox::questionTwoActions(parentWidgetOrView(),
xi18nc("@info",
"<para>The key has been copied to the card and a backup has been written to <filename>%1</filename>.</para>"
"<para>Do you want to delete the copy of the key stored on this computer?</para>",
backupFilename),
i18nc("@title:window", "Success"),
KGuiItem{i18nc("@action:button", "Delete copy on disk")},
KGuiItem{i18nc("@action:button", "Keep copy on disk")});
if (answer == KMessageBox::ButtonCode::PrimaryAction) {
// the user has created a backup; don't ask again for confirmation before deleting the copy on disk
startDeleteSecretKeyLocally(SkipConfirmation);
} else {
finished();
}
}
namespace
{
QString gnupgPrivateKeyBackupExtension()
{
return QStringLiteral(".gpgsk");
}
QString proposeFilename(const Subkey &subkey)
{
QString filename;
const auto key = subkey.parent();
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
const auto shortKeyID = Formatting::prettyKeyID(key.shortKeyID());
const auto shortSubkeyID = Formatting::prettyKeyID(QByteArray{subkey.keyID()}.right(8).constData());
const auto usage = Formatting::usageString(subkey).replace(QLatin1StringView{", "}, QLatin1String{"_"});
/* Not translated so it's better to use in tutorials etc. */
filename = ((shortKeyID == shortSubkeyID) //
? QStringView{u"%1_%2_SECRET_KEY_BACKUP_%3"}.arg(name, shortKeyID, usage)
: QStringView{u"%1_%2_SECRET_KEY_BACKUP_%3_%4"}.arg(name, shortKeyID, shortSubkeyID, usage));
filename.replace(u'/', u'_');
return QDir{ApplicationState::lastUsedExportDirectory()}.filePath(filename + gnupgPrivateKeyBackupExtension());
}
QString requestPrivateKeyBackupFilename(const QString &proposedFilename, QWidget *parent)
{
auto filename = FileDialog::getSaveFileNameEx(parent,
i18nc("@title:window", "Backup Secret Key"),
QStringLiteral("imp"),
proposedFilename,
i18nc("description of filename filter", "Secret Key Backup Files") + QLatin1StringView{" (*.gpgsk)"});
if (!filename.isEmpty()) {
const QFileInfo fi{filename};
if (fi.suffix().isEmpty()) {
filename += gnupgPrivateKeyBackupExtension();
}
ApplicationState::setLastUsedExportDirectory(filename);
}
return filename;
}
}
QString KeyToCardCommand::Private::backupKey()
{
static const QByteArray backupInfoName = "Backup-info:";
auto keydata = readSecretKeyFile();
if (keydata.empty()) {
return {};
}
- const auto filename = requestPrivateKeyBackupFilename(proposeFilename(subkey), parentWidgetOrView());
+ const auto filename = requestPrivateKeyBackupFilename(proposeFilename(subkeys[0]), parentWidgetOrView());
if (filename.isEmpty()) {
return {};
}
// remove old backup info
Kleo::erase_if(keydata, [](const auto &line) {
return line.startsWith(backupInfoName);
});
// prepend new backup info
const QByteArrayList backupInfo = {
backupInfoName,
- subkey.keyGrip(),
+ subkeys[0].keyGrip(),
QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toUtf8(),
"Kleopatra",
- Formatting::prettyNameAndEMail(subkey.parent()).toUtf8(),
+ Formatting::prettyNameAndEMail(subkeys[0].parent()).toUtf8(),
};
keydata.insert(keydata.begin(), backupInfo.join(' ') + '\n');
if (writeSecretKeyBackup(filename, keydata)) {
return filename;
} else {
return {};
}
}
std::vector<QByteArray> KeyToCardCommand::Private::readSecretKeyFile()
{
- const auto filename = QString::fromLatin1(subkey.keyGrip()) + QLatin1StringView{".key"};
+ const auto filename = QString::fromLatin1(subkeys[0].keyGrip()) + QLatin1StringView{".key"};
const auto path = QDir{Kleo::gnupgPrivateKeysDirectory()}.filePath(filename);
QFile file{path};
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
error(xi18n("Cannot open the private key file <filename>%1</filename> for reading.", path));
return {};
}
std::vector<QByteArray> lines;
while (!file.atEnd()) {
lines.push_back(file.readLine());
}
if (lines.empty()) {
error(xi18n("The private key file <filename>%1</filename> is empty.", path));
}
return lines;
}
bool KeyToCardCommand::Private::writeSecretKeyBackup(const QString &filename, const std::vector<QByteArray> &keydata)
{
QSaveFile file{filename};
// open the file in binary format because we want to write Unix line endings
if (!file.open(QIODevice::WriteOnly)) {
error(xi18n("Cannot open the file <filename>%1</filename> for writing.", filename));
return false;
}
for (const auto &line : keydata) {
file.write(line);
}
if (!file.commit()) {
error(xi18n("Writing the backup of the secret key to <filename>%1</filename> failed.", filename));
return false;
};
return true;
}
void KeyToCardCommand::Private::startDeleteSecretKeyLocally(Confirmation confirmation)
{
- const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
- if (!card) {
- error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
- finished();
- return;
- }
-
if (confirmation == AskForConfirmation) {
const auto answer = KMessageBox::questionTwoActions(parentWidgetOrView(),
xi18nc("@info", "Do you really want to delete the copy of the key stored on this computer?"),
i18nc("@title:window", "Confirm Deletion"),
KStandardGuiItem::del(),
KStandardGuiItem::cancel(),
{},
KMessageBox::Notify | KMessageBox::Dangerous);
if (answer != KMessageBox::ButtonCode::PrimaryAction) {
finished();
return;
}
}
- const auto cmd = QByteArray{"DELETE_KEY --force "} + subkey.keyGrip();
- ReaderStatus::mutableInstance()->startSimpleTransaction(card, cmd, q, [this](const GpgME::Error &err) {
- deleteSecretKeyLocallyFinished(err);
- });
+ remainingSubkeys = subkeys;
+
+ deleteNextKey();
}
void KeyToCardCommand::Private::deleteSecretKeyLocallyFinished(const GpgME::Error &err)
{
if (err) {
error(xi18nc("@info",
"<para>Failed to delete the copy of the key stored on this computer:</para><para><message>%1</message></para>",
Formatting::errorAsString(err)));
}
ReaderStatus::mutableInstance()->updateStatus();
- success(i18nc("@info", "Successfully deleted the copy of the key stored on this computer."));
finished();
}
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &subkey)
: CardCommand(new Private(this, subkey))
{
}
KeyToCardCommand::KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName)
: CardCommand(new Private(this, cardSlot, serialNumber, appName))
{
}
+KeyToCardCommand::KeyToCardCommand(QAbstractItemView *view, KeyListController *controller)
+ : CardCommand(new Private(this), view)
+{
+ Q_UNUSED(controller);
+}
+
KeyToCardCommand::~KeyToCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
}
namespace
{
bool cardSupportsKeyAlgorithm(const std::shared_ptr<const Card> &card, const std::string &keyAlgo)
{
if (card->appName() == OpenPGPCard::AppName) {
const auto pgpCard = static_cast<const OpenPGPCard *>(card.get());
const auto cardAlgos = pgpCard->supportedAlgorithms();
return std::ranges::any_of(cardAlgos, [keyAlgo](const auto &algo) {
return (keyAlgo == algo.id) //
|| (keyAlgo == OpenPGPCard::getAlgorithmName(algo.id, OpenPGPCard::pgpEncKeyRef()))
|| (keyAlgo == OpenPGPCard::getAlgorithmName(algo.id, OpenPGPCard::pgpSigKeyRef()));
});
}
return false;
}
}
// static
std::vector<std::shared_ptr<Card>> KeyToCardCommand::getSuitableCards(const GpgME::Subkey &subkey)
{
std::vector<std::shared_ptr<Card>> suitableCards;
if (subkey.isNull() || subkey.parent().protocol() != GpgME::OpenPGP) {
return suitableCards;
}
const auto keyAlgo = subkey.algoName();
Kleo::copy_if(ReaderStatus::instance()->getCards(), std::back_inserter(suitableCards), [keyAlgo](const auto &card) {
return cardSupportsKeyAlgorithm(card, keyAlgo);
});
return suitableCards;
}
-void KeyToCardCommand::Private::keyToCardDone(const GpgME::Error &err)
+std::vector<std::shared_ptr<Card>> KeyToCardCommand::getSuitableCards(const GpgME::Key &key)
{
- if (!err && !err.isCanceled()) {
- updateConnection = connect(ReaderStatus::instance(), &ReaderStatus::updateFinished, q, [this]() {
- updateDone();
+ std::vector<std::shared_ptr<Card>> suitableCards;
+ if (key.isNull() || key.protocol() != GpgME::OpenPGP) {
+ return suitableCards;
+ }
+ Kleo::copy_if(ReaderStatus::instance()->getCards(), std::back_inserter(suitableCards), [key](const auto &card) {
+ const auto &subkeys = key.subkeys();
+ return std::all_of(subkeys.begin(), subkeys.end(), [card](const auto &subkey) {
+ const auto keyAlgo = subkey.algoName();
+ return cardSupportsKeyAlgorithm(card, keyAlgo);
});
- ReaderStatus::mutableInstance()->updateCard(serialNumber(), appName);
+ });
+ return suitableCards;
+}
+
+void KeyToCardCommand::Private::keyToCardDone(const GpgME::Error &err)
+{
+ if (err.isCanceled()) {
+ finished();
return;
}
+
if (err) {
error(xi18nc("@info", "<para>Copying the key to the card failed:</para><para><message>%1</message></para>", Formatting::errorAsString(err)));
}
- finished();
+
+ updateConnection = connect(ReaderStatus::instance(), &ReaderStatus::updateFinished, q, [this]() {
+ updateDone();
+ });
+ ReaderStatus::mutableInstance()->updateCard(serialNumber(), appName);
}
void KeyToCardCommand::Private::keyToPIVCardDone(const GpgME::Error &err)
{
qCDebug(KLEOPATRA_LOG) << q << __func__ << Formatting::errorAsString(err) << "(" << err.code() << ")";
#ifdef GPG_ERROR_HAS_NO_AUTH
// gpgme 1.13 reports "BAD PIN" instead of "NO AUTH"
if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) {
authenticate();
return;
}
#endif
keyToCardDone(err);
}
void KeyToCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::doStart()";
d->start();
}
void KeyToCardCommand::doCancel()
{
}
+void KeyToCardCommand::Private::copyKey()
+{
+ const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
+ if (!card) {
+ error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
+ finished();
+ return;
+ }
+
+ if (card->appName() == SmartCard::OpenPGPCard::AppName) {
+ startKeyToOpenPGPCard();
+ } else if (card->appName() == SmartCard::PIVCard::AppName) {
+ startKeyToPIVCard();
+ } else {
+ error(xi18nc("@info", "Sorry! Writing keys to the card <emphasis>%1</emphasis> is not supported.", cardDisplayName(card)));
+ finished();
+ return;
+ }
+}
+
+void KeyToCardCommand::Private::nextSubkey()
+{
+ const auto subkey = remainingSubkeys[remainingSubkeys.size() - 1];
+ remainingSubkeys.pop_back();
+ auto cardSlot = getOpenPGPCardSlotForKey(subkey, parentWidgetOrView());
+ if (cardSlot.empty()) {
+ finished();
+ return;
+ }
+
+ gripsAndSlots.push_back(GripAndSlot{
+ std::string(subkey.keyGrip()),
+ cardSlot,
+ });
+
+ const auto pgpCard = SmartCard::ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
+ if (!pgpCard) {
+ error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber())));
+ finished();
+ return;
+ }
+
+ // Check if we need to do the overwrite warning.
+ const std::string existingKey = pgpCard->keyFingerprint(cardSlot);
+ if (!existingKey.empty()) {
+ const auto encKeyWarning = (cardSlot == OpenPGPCard::pgpEncKeyRef())
+ ? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
+ : QString{};
+ const QString message = i18nc("@info",
+ "<p>The card <em>%1</em> already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
+ "<p>If there is no backup the existing key will be irrecoverably lost.</p>",
+ cardDisplayName(pgpCard))
+ + i18n("The existing key has the fingerprint:") + QStringLiteral("<pre>%1</pre>").arg(Formatting::prettyID(existingKey.c_str())) + encKeyWarning;
+ const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(),
+ message,
+ i18nc("@title:window", "Overwrite existing key"),
+ KGuiItem{i18nc("@action:button", "Overwrite Existing Key")},
+ KStandardGuiItem::cancel(),
+ QString(),
+ KMessageBox::Notify | KMessageBox::Dangerous);
+ if (choice != KMessageBox::Continue) {
+ finished();
+ return;
+ }
+ }
+
+ // Now do the deed
+ const auto time = QDateTime::fromSecsSinceEpoch(quint32(subkey.creationTime()), QTimeZone::utc());
+ const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss"));
+ const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 %3 %4")
+ .arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()), QString::fromStdString(cardSlot), timestamp);
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, cmd.toUtf8(), q_func(), [this](const GpgME::Error &err) {
+ if (remainingSubkeys.size() > 0) {
+ QMetaObject::invokeMethod(
+ q,
+ [this]() {
+ nextSubkey();
+ },
+ Qt::QueuedConnection);
+ } else {
+ keyToCardDone(err);
+ }
+ });
+}
+
+void KeyToCardCommand::Private::deleteNextKey()
+{
+ const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
+ if (!card) {
+ error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
+ finished();
+ return;
+ }
+ const auto subkey = remainingSubkeys[remainingSubkeys.size() - 1];
+ remainingSubkeys.pop_back();
+
+ const auto cmd = QByteArray{"DELETE_KEY --force "} + subkey.keyGrip();
+ ReaderStatus::mutableInstance()->startSimpleTransaction(card, cmd, q, [this](const GpgME::Error &err) {
+ if (err) {
+ deleteSecretKeyLocallyFinished(err);
+ } else {
+ if (remainingSubkeys.empty()) {
+ deleteSecretKeyLocallyFinished(err);
+ } else {
+ QMetaObject::invokeMethod(
+ q,
+ [this]() {
+ deleteNextKey();
+ },
+ Qt::QueuedConnection);
+ }
+ }
+ });
+}
+
#undef q_func
#undef d_func
#include "moc_keytocardcommand.cpp"
diff --git a/src/commands/keytocardcommand.h b/src/commands/keytocardcommand.h
index a93f5aae9..43487a1e3 100644
--- a/src/commands/keytocardcommand.h
+++ b/src/commands/keytocardcommand.h
@@ -1,54 +1,62 @@
/* commands/keytocardcommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
+#include "commands/command.h"
#include <commands/cardcommand.h>
#include <gpgme++/key.h>
#include <memory>
namespace Kleo
{
namespace SmartCard
{
class Card;
}
}
namespace Kleo
{
namespace Commands
{
class KeyToCardCommand : public CardCommand
{
Q_OBJECT
public:
KeyToCardCommand(const GpgME::Subkey &subkey);
KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName);
+ KeyToCardCommand(QAbstractItemView *view, KeyListController *controller);
~KeyToCardCommand() override;
+ /* reimp */ static Restrictions restrictions()
+ {
+ return NeedSelection | OnlyOneKey | NeedSecretKey | SuitableForCard | MustBeOpenPGP | MustBeValid;
+ }
+
static std::vector<std::shared_ptr<Kleo::SmartCard::Card>> getSuitableCards(const GpgME::Subkey &subkey);
+ static std::vector<std::shared_ptr<Kleo::SmartCard::Card>> getSuitableCards(const GpgME::Key &key);
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
}
}
diff --git a/src/dialogs/copytosmartcarddialog.cpp b/src/dialogs/copytosmartcarddialog.cpp
new file mode 100644
index 000000000..061eb6635
--- /dev/null
+++ b/src/dialogs/copytosmartcarddialog.cpp
@@ -0,0 +1,169 @@
+// SPDX-FileCopyrightText: 2024 g10 Code GmbH
+// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "copytosmartcarddialog.h"
+
+#include <Libkleo/Formatting>
+
+#include <KLocalizedString>
+
+#include <QButtonGroup>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+using namespace Kleo;
+using namespace Kleo::Dialogs;
+
+class CopyToSmartcardDialog::Private
+{
+ friend class ::Kleo::Dialogs::CopyToSmartcardDialog;
+ CopyToSmartcardDialog *const q;
+
+public:
+ explicit Private(CopyToSmartcardDialog *qq);
+
+private:
+ GpgME::Key key;
+ QString cardDisplayName;
+
+ struct UI {
+ QLabel *label = nullptr;
+ QDialogButtonBox *buttonBox = nullptr;
+ QRadioButton *deleteRadio = nullptr;
+ QRadioButton *fileBackupRadio = nullptr;
+ QRadioButton *printBackupRadio = nullptr;
+ QRadioButton *existingBackupRadio = nullptr;
+ QRadioButton *keepRadio = nullptr;
+
+ QPushButton *acceptButton = nullptr;
+ } ui;
+
+ void setUpUI(CopyToSmartcardDialog *q)
+ {
+ auto layout = new QVBoxLayout;
+ q->setLayout(layout);
+
+ ui.label = new QLabel;
+ layout->addWidget(ui.label);
+
+ layout->addStretch(1);
+
+ ui.deleteRadio = new QRadioButton(i18nc("@option:radio", "Delete key from disk."), q);
+ ui.keepRadio = new QRadioButton(i18nc("@option:radio", "Keep key on disk."), q);
+
+ auto spacingLayout = new QHBoxLayout;
+ spacingLayout->addSpacing(32);
+ auto backupLayout = new QVBoxLayout;
+ spacingLayout->addLayout(backupLayout);
+
+ ui.fileBackupRadio = new QRadioButton(i18nc("@option:radio", "Make a backup of the secret key to a file."));
+ ui.printBackupRadio = new QRadioButton(i18nc("@option:radio", "Make a printed backup of the secret key."));
+ ui.existingBackupRadio = new QRadioButton(i18nc("@option:radio", "I already have a backup of the secret key."));
+
+ ui.fileBackupRadio->setEnabled(false);
+ ui.printBackupRadio->setEnabled(false);
+ ui.existingBackupRadio->setEnabled(false);
+
+ connect(ui.deleteRadio, &QRadioButton::toggled, ui.fileBackupRadio, &QRadioButton::setEnabled);
+ connect(ui.deleteRadio, &QRadioButton::toggled, ui.printBackupRadio, &QRadioButton::setEnabled);
+ connect(ui.deleteRadio, &QRadioButton::toggled, ui.existingBackupRadio, &QRadioButton::setEnabled);
+
+ connect(ui.deleteRadio, &QRadioButton::toggled, q, &CopyToSmartcardDialog::checkAcceptable);
+ connect(ui.fileBackupRadio, &QRadioButton::toggled, q, &CopyToSmartcardDialog::checkAcceptable);
+ connect(ui.printBackupRadio, &QRadioButton::toggled, q, &CopyToSmartcardDialog::checkAcceptable);
+ connect(ui.existingBackupRadio, &QRadioButton::toggled, q, &CopyToSmartcardDialog::checkAcceptable);
+ connect(ui.keepRadio, &QRadioButton::toggled, q, &CopyToSmartcardDialog::checkAcceptable);
+ auto buttons = new QButtonGroup(q);
+ buttons->addButton(ui.fileBackupRadio);
+ buttons->addButton(ui.printBackupRadio);
+ buttons->addButton(ui.existingBackupRadio);
+
+ backupLayout->addWidget(ui.fileBackupRadio);
+ backupLayout->addWidget(ui.printBackupRadio);
+ backupLayout->addWidget(ui.existingBackupRadio);
+
+ layout->addWidget(ui.deleteRadio);
+ layout->addLayout(spacingLayout);
+ layout->addWidget(ui.keepRadio);
+
+ ui.buttonBox = new QDialogButtonBox;
+ auto cancelButton = ui.buttonBox->addButton(QDialogButtonBox::Cancel);
+ connect(cancelButton, &QPushButton::clicked, q, &QDialog::reject);
+ ui.acceptButton = ui.buttonBox->addButton(i18nc("@action:button", "Copy to Card"), QDialogButtonBox::AcceptRole);
+ connect(ui.acceptButton, &QPushButton::clicked, q, &QDialog::accept);
+ ui.acceptButton->setEnabled(false);
+ ui.acceptButton->setIcon(QIcon::fromTheme(QStringLiteral("auth-sim-locked")));
+ layout->addWidget(ui.buttonBox);
+ }
+
+ void update();
+};
+
+CopyToSmartcardDialog::Private::Private(CopyToSmartcardDialog *qq)
+ : q(qq)
+{
+ setUpUI(q);
+}
+
+CopyToSmartcardDialog::CopyToSmartcardDialog(QWidget *parent)
+ : QDialog(parent)
+ , d(new Private(this))
+{
+ setWindowTitle(i18nc("@title:dialog", "Copy Key to Smartcard"));
+}
+
+CopyToSmartcardDialog::~CopyToSmartcardDialog() = default;
+
+GpgME::Key CopyToSmartcardDialog::key() const
+{
+ return d->key;
+}
+
+void CopyToSmartcardDialog::setKey(const GpgME::Key &key)
+{
+ d->key = key;
+ d->update();
+}
+
+void CopyToSmartcardDialog::Private::update()
+{
+ ui.label->setText(xi18nc("@info",
+ "<para>Selected Key: <emphasis>%1</emphasis></para><para>Selected Smartcard: <emphasis>%2</emphasis></para><para>Choose one of "
+ "the following options to continue:</para>",
+ Formatting::summaryLine(key),
+ cardDisplayName));
+}
+
+QString CopyToSmartcardDialog::cardDisplayName() const
+{
+ return d->cardDisplayName;
+}
+
+void CopyToSmartcardDialog::setCardDisplayName(const QString &cardDisplayName)
+{
+ d->cardDisplayName = cardDisplayName;
+ d->update();
+}
+
+void CopyToSmartcardDialog::checkAcceptable()
+{
+ d->ui.acceptButton->setEnabled(
+ d->ui.keepRadio->isChecked()
+ || d->ui.deleteRadio && (d->ui.fileBackupRadio->isChecked() || d->ui.printBackupRadio->isChecked() || d->ui.existingBackupRadio->isChecked()));
+}
+
+CopyToSmartcardDialog::BackupChoice CopyToSmartcardDialog::backupChoice() const
+{
+ if (d->ui.keepRadio->isChecked()) {
+ return BackupChoice::KeepKey;
+ } else if (d->ui.fileBackupRadio->isChecked()) {
+ return BackupChoice::FileBackup;
+ } else if (d->ui.printBackupRadio->isChecked()) {
+ return BackupChoice::PrintBackup;
+ }
+ return BackupChoice::ExistingBackup;
+}
diff --git a/src/dialogs/copytosmartcarddialog.h b/src/dialogs/copytosmartcarddialog.h
new file mode 100644
index 000000000..e06f3b296
--- /dev/null
+++ b/src/dialogs/copytosmartcarddialog.h
@@ -0,0 +1,46 @@
+// 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>
+
+#include <gpgme++/key.h>
+
+namespace Kleo
+{
+namespace Dialogs
+{
+
+class CopyToSmartcardDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ enum BackupChoice {
+ FileBackup,
+ PrintBackup,
+ ExistingBackup,
+ KeepKey,
+ };
+
+ explicit CopyToSmartcardDialog(QWidget *parent = nullptr);
+ ~CopyToSmartcardDialog() override;
+
+ GpgME::Key key() const;
+ void setKey(const GpgME::Key &key);
+
+ QString cardDisplayName() const;
+ void setCardDisplayName(const QString &cardDisplayName);
+
+ BackupChoice backupChoice() const;
+
+private:
+ class Private;
+ const std::unique_ptr<Private> d;
+
+ void checkAcceptable();
+};
+
+}
+}
diff --git a/src/kleopatra.rc b/src/kleopatra.rc
index bc8facc76..ef5379ece 100644
--- a/src/kleopatra.rc
+++ b/src/kleopatra.rc
@@ -1,162 +1,165 @@
<!DOCTYPE gui >
-<gui name="kleopatra" version="515" >
+<gui name="kleopatra" version="516" >
<MenuBar>
<Menu name="file">
<text>&amp;File</text>
<Action name="file_new_certificate"/>
<Action name="file_new_certificate_signing_request"/>
<Separator/>
<Action name="file_lookup_certificates"/>
<Action name="file_import_certificates"/>
<Separator/>
<Action name="file_export_certificates"/>
<Action name="file_export_secret_keys"/>
<Action name="file_export_paper_key"/>
<Action name="file_export_certificates_to_server"/>
<Action name="file_export_certificate_to_provider"/>
<Separator/>
<Action name="file_decrypt_verify_files"/>
<Action name="file_sign_encrypt_files"/>
<Action name="file_sign_encrypt_folder"/>
<Separator/>
<Action name="file_checksum_create_files"/>
<Action name="file_checksum_verify_files"/>
<Separator/>
<Action name="quit" />
</Menu>
<Menu name="view">
<text>&amp;View</text>
<Action name="view_redisplay"/>
<Separator/>
<Action name="view_stop_operations"/>
<Action name="view_certificate_details"/>
<Separator/>
<Action name="window_view_hierarchical"/>
<Separator/>
<Action name="window_expand_all"/>
<Action name="window_collapse_all"/>
<Separator/>
<Action name="view_certificate_overview"/>
<Action name="pad_view"/>
<Action name="manage_smartcard"/>
</Menu>
<Menu name="certMenu">
<text>&amp;Certificates</text>
<Action name="certificates_refresh"/>
<Separator/>
<Action name="certificates_change_owner_trust"/>
<Action name="certificates_trust_root"/>
<Action name="certificates_distrust_root"/>
<Separator/>
<Action name="certificates_certify_certificate"/>
<Action name="certificates_revoke_certification"/>
<Separator/>
<Action name="certificates_change_expiry"/>
<Action name="certificates_change_passphrase"/>
<Action name="certificates_add_userid"/>
<Separator/>
<Action name="certificates_revoke"/>
<Action name="certificates_delete"/>
<Separator/>
<Action name="certificates_create_group"/>
+ <Separator/>
+ <Action name="certificate_to_card"/>
</Menu>
<Menu name="tools">
<text>&amp;Tools</text>
<Action name="tools_start_kwatchgnupg"/>
<Separator/>
<Action name="tools_refresh_x509_certificates"/>
<Action name="tools_refresh_openpgp_certificates"/>
<Separator/>
<Action name="clipboard_menu"/>
<Separator/>
<Action name="crl_import_crl"/>
<Separator/>
<Action name="crl_clear_crl_cache"/>
<Action name="crl_dump_crl_cache"/>
<Separator/>
<Action name="tools_restart_backend"/>
</Menu>
<Menu name="settings">
<text>&amp;Settings</text>
<Action name="settings_self_test"/>
<Action name="configure_groups" append="configure_merge"/>
</Menu>
<Menu name="window" append="settings_merge">
<text>&amp;Window</text>
<Action name="window_rename_tab"/>
<Separator/>
<Action name="window_new_tab"/>
<Action name="window_duplicate_tab"/>
<Action name="window_close_tab"/>
<Separator/>
<Action name="window_move_tab_left"/>
<Action name="window_move_tab_right"/>
</Menu>
<Menu name="help">
<text>&amp;Help</text>
<Action name="help_doc_quickguide"/>
<Action name="help_doc_symenc"/>
<Action name="help_doc_groups"/>
<Action name="help_doc_gpgol"/>
<Menu name="help_more">
<text>&amp;More documentation</text>
<Action name="help_doc_cert_management"/>
<Action name="help_doc_smartcard"/>
<Action name="help_doc_gnupg"/>
</Menu>
<Separator/>
<Action name="help_doc_compendium"/>
<Action name="help_doc_approval_manual"/>
<Action name="help_doc_vsa10573"/>
<Action name="help_doc_vsa10584"/>
<Separator/>
<Action name="help_check_updates"/>
</Menu>
</MenuBar>
<ToolBar fullWidth="false" name="mainToolBar" iconText="TextUnderIcon">
<text>Main Toolbar</text>
<Action name="file_sign_encrypt_files"/>
<Action name="file_decrypt_verify_files"/>
<Separator/>
<Action name="file_import_certificates"/>
<Action name="file_export_certificates"/>
<Action name="certificates_certify_certificate"/>
<Action name="file_lookup_certificates"/>
<Separator/>
<Action name="view_certificate_overview"/>
<Action name="pad_view"/>
<Action name="manage_smartcard"/>
<Separator/>
<Action name="configure_groups_toolbar"/>
</ToolBar>
<Menu name="listview_popup">
<text>&amp;Certificates</text>
<Action name="certificates_refresh"/>
<Separator/>
<Action name="certificates_certify_certificate"/>
<Action name="certificates_revoke_certification"/>
<Action name="certificates_trust_root"/>
<Action name="certificates_distrust_root"/>
<Action name="certificates_change_owner_trust"/>
<Separator/>
<Action name="certificates_change_expiry"/>
<Action name="certificates_change_passphrase"/>
<Action name="certificates_add_userid"/>
<Separator/>
<Action name="certificates_revoke"/>
<Action name="certificates_delete"/>
<Separator/>
<Action name="certificates_create_group"/>
<Separator/>
<Action name="file_export_certificates"/>
<Action name="file_export_secret_keys"/>
<Action name="file_export_paper_key"/>
<Action name="file_export_certificates_to_server"/>
<Action name="file_export_certificate_to_provider"/>
+ <Action name="certificate_to_card"/>
<Separator/>
<Action name="view_certificate_details"/>
</Menu>
</gui>
diff --git a/src/view/keylistcontroller.cpp b/src/view/keylistcontroller.cpp
index cc686cdcc..1c10ba012 100644
--- a/src/view/keylistcontroller.cpp
+++ b/src/view/keylistcontroller.cpp
@@ -1,1033 +1,1086 @@
/* -*- mode: c++; c-basic-offset:4 -*-
view/keylistcontroller.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keylistcontroller.h"
#include "tabwidget.h"
#include <smartcard/readerstatus.h>
#include <utils/action_data.h>
#include "commands/exportcertificatecommand.h"
#include "commands/exportopenpgpcertstoservercommand.h"
#include "kleopatra_debug.h"
#include "tooltippreferences.h"
#include <settings.h>
#ifdef MAILAKONADI_ENABLED
#include "commands/exportopenpgpcerttoprovidercommand.h"
#endif // MAILAKONADI_ENABLED
#include "commands/adduseridcommand.h"
#include "commands/certifycertificatecommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/changeownertrustcommand.h"
#include "commands/changepassphrasecommand.h"
#include "commands/changeroottrustcommand.h"
#include "commands/checksumcreatefilescommand.h"
#include "commands/checksumverifyfilescommand.h"
#include "commands/clearcrlcachecommand.h"
#include "commands/creategroupcommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/deletecertificatescommand.h"
#include "commands/detailscommand.h"
#include "commands/dumpcertificatecommand.h"
#include "commands/dumpcrlcachecommand.h"
#include "commands/exportpaperkeycommand.h"
#include "commands/exportsecretkeycommand.h"
#include "commands/importcertificatefromfilecommand.h"
#include "commands/importcrlcommand.h"
+#include "commands/keytocardcommand.h"
#include "commands/lookupcertificatescommand.h"
#include "commands/newcertificatesigningrequestcommand.h"
#include "commands/newopenpgpcertificatecommand.h"
#include "commands/refreshcertificatescommand.h"
#include "commands/refreshopenpgpcertscommand.h"
#include "commands/refreshx509certscommand.h"
#include "commands/reloadkeyscommand.h"
#include "commands/revokecertificationcommand.h"
#include "commands/revokekeycommand.h"
#include "commands/signencryptfilescommand.h"
#include "commands/signencryptfoldercommand.h"
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyListModel>
#include <gpgme++/key.h>
#include <KActionCollection>
#include <KLocalizedString>
#include <QAbstractItemView>
#include <QAction>
#include <QItemSelectionModel>
#include <QPointer>
#include <algorithm>
#include <iterator>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
namespace ranges = std::ranges;
class KeyListController::Private
{
friend class ::Kleo::KeyListController;
KeyListController *const q;
public:
explicit Private(KeyListController *qq);
~Private();
void connectView(QAbstractItemView *view);
void connectCommand(Command *cmd);
void connectTabWidget();
void disconnectTabWidget();
void addCommand(Command *cmd)
{
connectCommand(cmd);
commands.insert(ranges::lower_bound(commands, cmd), cmd);
}
void addView(QAbstractItemView *view)
{
connectView(view);
views.insert(ranges::lower_bound(views, view), view);
}
void removeView(QAbstractItemView *view)
{
view->disconnect(q);
view->selectionModel()->disconnect(q);
std::erase(views, view);
}
public:
void slotDestroyed(QObject *o)
{
qCDebug(KLEOPATRA_LOG) << (void *)o;
std::erase(views, o);
std::erase(commands, o);
}
void slotDoubleClicked(const QModelIndex &idx);
void slotActivated(const QModelIndex &idx);
void slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_);
void slotContextMenu(const QPoint &pos);
void slotCommandFinished();
void slotActionTriggered(QAction *action);
void slotCurrentViewChanged(QAbstractItemView *view)
{
if (view && !ranges::binary_search(views, view)) {
qCDebug(KLEOPATRA_LOG) << "you need to register view" << view << "before trying to set it as the current view!";
addView(view);
}
currentView = view;
q->enableDisableActions(view ? view->selectionModel() : nullptr);
}
private:
int toolTipOptions() const;
private:
static Command::Restrictions calculateRestrictionsMask(const QItemSelectionModel *sm);
private:
struct action_item {
QPointer<QAction> action;
Command::Restrictions restrictions;
Command *(*createCommand)(QAbstractItemView *, KeyListController *);
};
std::vector<action_item> actions;
std::vector<QAbstractItemView *> views;
std::vector<Command *> commands;
QPointer<QWidget> parentWidget;
QPointer<TabWidget> tabWidget;
QPointer<QAbstractItemView> currentView;
QPointer<AbstractKeyListModel> flatModel, hierarchicalModel;
std::vector<QMetaObject::Connection> m_connections;
};
KeyListController::Private::Private(KeyListController *qq)
: q(qq)
, actions()
, views()
, commands()
, parentWidget()
, tabWidget()
, flatModel()
, hierarchicalModel()
{
}
KeyListController::Private::~Private()
{
}
KeyListController::KeyListController(QObject *p)
: QObject(p)
, d(new Private(this))
{
}
KeyListController::~KeyListController()
{
}
void KeyListController::addView(QAbstractItemView *view)
{
if (!view || ranges::binary_search(d->views, view)) {
return;
}
d->addView(view);
}
void KeyListController::removeView(QAbstractItemView *view)
{
if (!view || !ranges::binary_search(d->views, view)) {
return;
}
d->removeView(view);
}
void KeyListController::setCurrentView(QAbstractItemView *view)
{
d->slotCurrentViewChanged(view);
}
std::vector<QAbstractItemView *> KeyListController::views() const
{
return d->views;
}
void KeyListController::setFlatModel(AbstractKeyListModel *model)
{
if (model == d->flatModel) {
return;
}
d->flatModel = model;
if (model) {
model->setToolTipOptions(d->toolTipOptions());
}
}
void KeyListController::setHierarchicalModel(AbstractKeyListModel *model)
{
if (model == d->hierarchicalModel) {
return;
}
d->hierarchicalModel = model;
if (model) {
model->setToolTipOptions(d->toolTipOptions());
}
}
void KeyListController::setTabWidget(TabWidget *tabWidget)
{
if (tabWidget == d->tabWidget) {
return;
}
d->disconnectTabWidget();
d->tabWidget = tabWidget;
d->connectTabWidget();
d->slotCurrentViewChanged(tabWidget ? tabWidget->currentView() : nullptr);
}
void KeyListController::setParentWidget(QWidget *parent)
{
d->parentWidget = parent;
}
QWidget *KeyListController::parentWidget() const
{
return d->parentWidget;
}
void KeyListController::Private::connectTabWidget()
{
if (!tabWidget) {
return;
}
const auto views = tabWidget->views();
ranges::for_each(views, [this](QAbstractItemView *view) {
addView(view);
});
m_connections.reserve(3);
m_connections.push_back(connect(tabWidget, &TabWidget::viewAdded, q, &KeyListController::addView));
m_connections.push_back(connect(tabWidget, &TabWidget::viewAboutToBeRemoved, q, &KeyListController::removeView));
m_connections.push_back(connect(tabWidget, &TabWidget::currentViewChanged, q, [this](QAbstractItemView *view) {
slotCurrentViewChanged(view);
}));
}
void KeyListController::Private::disconnectTabWidget()
{
if (!tabWidget) {
return;
}
for (const auto &connection : m_connections) {
disconnect(connection);
}
m_connections.clear();
const auto views = tabWidget->views();
ranges::for_each(views, [this](QAbstractItemView *view) {
removeView(view);
});
}
AbstractKeyListModel *KeyListController::flatModel() const
{
return d->flatModel;
}
AbstractKeyListModel *KeyListController::hierarchicalModel() const
{
return d->hierarchicalModel;
}
QAbstractItemView *KeyListController::currentView() const
{
return d->currentView;
}
TabWidget *KeyListController::tabWidget() const
{
return d->tabWidget;
}
void KeyListController::createActions(KActionCollection *coll)
{
const std::vector<action_data> common_and_openpgp_action_data = {
// File menu
{
"file_new_certificate",
i18n("New OpenPGP Key Pair..."),
i18n("Create a new OpenPGP certificate"),
"view-certificate-add",
nullptr,
nullptr,
QStringLiteral("Ctrl+N"),
},
{
"file_export_certificates",
i18n("Export..."),
i18n("Export the selected certificate (public key) to a file"),
"view-certificate-export",
nullptr,
nullptr,
QStringLiteral("Ctrl+E"),
},
{
"file_export_certificates_to_server",
i18n("Publish on Server..."),
i18n("Publish the selected certificate (public key) on a public keyserver"),
"view-certificate-export-server",
nullptr,
nullptr,
QStringLiteral("Ctrl+Shift+E"),
},
#ifdef MAILAKONADI_ENABLED
{
"file_export_certificate_to_provider",
i18n("Publish at Mail Provider..."),
i18n("Publish the selected certificate (public key) at mail provider's Web Key Directory if offered"),
"view-certificate-export",
nullptr,
nullptr,
QString(),
},
#endif // MAILAKONADI_ENABLED
{
"file_export_secret_keys",
i18n("Backup Secret Keys..."),
QString(),
"view-certificate-export-secret",
nullptr,
nullptr,
QString(),
},
{
"file_export_paper_key",
i18n("Print Secret Key..."),
QString(),
"document-print",
nullptr,
nullptr,
QString(),
},
{
"file_lookup_certificates",
i18n("Lookup on Server..."),
i18n("Search for certificates online using a public keyserver"),
"edit-find",
nullptr,
nullptr,
QStringLiteral("Shift+Ctrl+I"),
},
{
"file_import_certificates",
i18n("Import..."),
i18n("Import a certificate from a file"),
"view-certificate-import",
nullptr,
nullptr,
QStringLiteral("Ctrl+I"),
},
{
"file_decrypt_verify_files",
i18n("Decrypt/Verify..."),
i18n("Decrypt and/or verify files"),
"document-edit-decrypt-verify",
nullptr,
nullptr,
QString(),
},
{
"file_sign_encrypt_files",
i18n("Sign/Encrypt..."),
i18n("Encrypt and/or sign files"),
"document-edit-sign-encrypt",
nullptr,
nullptr,
QString(),
},
{
"file_sign_encrypt_folder",
i18n("Sign/Encrypt Folder..."),
i18n("Encrypt and/or sign folders"),
"folder-edit-sign-encrypt-symbolic",
nullptr,
nullptr,
QString(),
},
{
"file_checksum_create_files",
i18n("Create Checksum Files..."),
QString(),
nullptr /*"document-checksum-create"*/,
nullptr,
nullptr,
QString(),
},
{
"file_checksum_verify_files",
i18n("Verify Checksum Files..."),
QString(),
nullptr /*"document-checksum-verify"*/,
nullptr,
nullptr,
QString(),
},
// View menu
{
"view_redisplay",
i18n("Redisplay"),
QString(),
"view-refresh",
nullptr,
nullptr,
QStringLiteral("F5"),
},
{
"view_stop_operations",
i18n("Stop Operation"),
QString(),
"process-stop",
this,
[this](bool) {
cancelCommands();
},
QStringLiteral("Escape"),
RegularQAction,
Disabled,
},
{
"view_certificate_details",
i18n("Details"),
QString(),
"dialog-information",
nullptr,
nullptr,
QString(),
},
// Certificate menu
{
"certificates_revoke",
i18n("Revoke Certificate..."),
i18n("Revoke the selected OpenPGP certificate"),
"view-certificate-revoke",
nullptr,
nullptr,
{},
},
{
"certificates_delete",
i18n("Delete"),
i18n("Delete selected certificates"),
"edit-delete",
nullptr,
nullptr,
QStringLiteral("Delete"),
},
{
"certificates_refresh",
i18n("Update Certificates"),
i18n("Update selected certificates"),
"view-refresh",
nullptr,
nullptr,
QString(),
},
{
"certificates_certify_certificate",
i18n("Certify..."),
i18n("Certify the validity of the selected certificate"),
"view-certificate-sign",
nullptr,
nullptr,
QString(),
},
{
"certificates_revoke_certification",
i18n("Revoke Certification..."),
i18n("Revoke the certification of the selected certificate"),
"view-certificate-revoke",
nullptr,
nullptr,
QString(),
},
{
"certificates_change_expiry",
i18n("Change End of Validity Period..."),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_change_owner_trust",
i18nc("@action:inmenu", "Change Certification Power..."),
i18nc("@info:tooltip", "Grant or revoke the certification power of the selected certificate"),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_change_passphrase",
i18n("Change Passphrase..."),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_add_userid",
i18n("Add User ID..."),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_create_group",
i18nc("@action:inmenu", "Create Group..."),
i18nc("@info:tooltip", "Create a group from the selected certificates"),
"resource-group-new",
nullptr,
nullptr,
QString(),
},
+ {
+ "certificate_to_card",
+ i18nc("@action:inmenu", "Copy to Card"),
+ i18nc("@info:tooltip", "Copy the selected certificate to a smartcard"),
+ "auth-sim-locked",
+ nullptr,
+ nullptr,
+ QString(),
+ },
// Tools menu
{
"tools_refresh_openpgp_certificates",
i18n("Refresh OpenPGP Certificates"),
QString(),
"view-refresh",
nullptr,
nullptr,
QString(),
},
// Window menu
// (come from TabWidget)
// Help menu
// (come from MainWindow)
};
static const action_data cms_create_csr_action_data = {
"file_new_certificate_signing_request",
i18n("New S/MIME Certification Request..."),
i18n("Create a new S/MIME certificate signing request (CSR)"),
"view-certificate-add",
nullptr,
nullptr,
{},
};
static const std::vector<action_data> cms_action_data = {
// Certificate menu
{
"certificates_trust_root",
i18n("Trust Root Certificate"),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_distrust_root",
i18n("Distrust Root Certificate"),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"certificates_dump_certificate",
i18n("Technical Details"),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
// Tools menu
{
"tools_refresh_x509_certificates",
i18n("Refresh S/MIME Certificates"),
QString(),
"view-refresh",
nullptr,
nullptr,
QString(),
},
{
"crl_clear_crl_cache",
i18n("Clear CRL Cache"),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"crl_dump_crl_cache",
i18n("Dump CRL Cache"),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
{
"crl_import_crl",
i18n("Import CRL From File..."),
QString(),
nullptr,
nullptr,
nullptr,
QString(),
},
};
std::vector<action_data> action_data = common_and_openpgp_action_data;
if (const Kleo::Settings settings{}; settings.cmsEnabled()) {
if (settings.cmsCertificateCreationAllowed()) {
action_data.push_back(cms_create_csr_action_data);
}
action_data.reserve(action_data.size() + cms_action_data.size());
ranges::copy(cms_action_data, std::back_inserter(action_data));
}
make_actions_from_data(action_data, coll);
if (QAction *action = coll->action(QStringLiteral("view_stop_operations"))) {
connect(this, &KeyListController::commandsExecuting, action, &QAction::setEnabled);
}
// ### somehow make this better...
registerActionForCommand<NewOpenPGPCertificateCommand>(coll->action(QStringLiteral("file_new_certificate")));
registerActionForCommand<NewCertificateSigningRequestCommand>(coll->action(QStringLiteral("file_new_certificate_signing_request")));
//---
registerActionForCommand<LookupCertificatesCommand>(coll->action(QStringLiteral("file_lookup_certificates")));
registerActionForCommand<ImportCertificateFromFileCommand>(coll->action(QStringLiteral("file_import_certificates")));
//---
registerActionForCommand<ExportCertificateCommand>(coll->action(QStringLiteral("file_export_certificates")));
registerActionForCommand<ExportSecretKeyCommand>(coll->action(QStringLiteral("file_export_secret_keys")));
registerActionForCommand<ExportPaperKeyCommand>(coll->action(QStringLiteral("file_export_paper_key")));
registerActionForCommand<ExportOpenPGPCertsToServerCommand>(coll->action(QStringLiteral("file_export_certificates_to_server")));
#ifdef MAILAKONADI_ENABLED
registerActionForCommand<ExportOpenPGPCertToProviderCommand>(coll->action(QStringLiteral("file_export_certificate_to_provider")));
#endif // MAILAKONADI_ENABLED
+ registerActionForCommand<KeyToCardCommand>(coll->action(QStringLiteral("certificate_to_card")));
//---
registerActionForCommand<DecryptVerifyFilesCommand>(coll->action(QStringLiteral("file_decrypt_verify_files")));
registerActionForCommand<SignEncryptFilesCommand>(coll->action(QStringLiteral("file_sign_encrypt_files")));
registerActionForCommand<SignEncryptFolderCommand>(coll->action(QStringLiteral("file_sign_encrypt_folder")));
//---
registerActionForCommand<ChecksumCreateFilesCommand>(coll->action(QStringLiteral("file_checksum_create_files")));
registerActionForCommand<ChecksumVerifyFilesCommand>(coll->action(QStringLiteral("file_checksum_verify_files")));
registerActionForCommand<ReloadKeysCommand>(coll->action(QStringLiteral("view_redisplay")));
// coll->action( "view_stop_operations" ) <-- already dealt with in make_actions_from_data()
registerActionForCommand<DetailsCommand>(coll->action(QStringLiteral("view_certificate_details")));
registerActionForCommand<ChangeOwnerTrustCommand>(coll->action(QStringLiteral("certificates_change_owner_trust")));
registerActionForCommand<TrustRootCommand>(coll->action(QStringLiteral("certificates_trust_root")));
registerActionForCommand<DistrustRootCommand>(coll->action(QStringLiteral("certificates_distrust_root")));
//---
registerActionForCommand<CertifyCertificateCommand>(coll->action(QStringLiteral("certificates_certify_certificate")));
if (RevokeCertificationCommand::isSupported()) {
registerActionForCommand<RevokeCertificationCommand>(coll->action(QStringLiteral("certificates_revoke_certification")));
}
//---
registerActionForCommand<ChangeExpiryCommand>(coll->action(QStringLiteral("certificates_change_expiry")));
registerActionForCommand<ChangePassphraseCommand>(coll->action(QStringLiteral("certificates_change_passphrase")));
registerActionForCommand<AddUserIDCommand>(coll->action(QStringLiteral("certificates_add_userid")));
registerActionForCommand<CreateGroupCommand>(coll->action(QStringLiteral("certificates_create_group")));
//---
registerActionForCommand<RevokeKeyCommand>(coll->action(QStringLiteral("certificates_revoke")));
registerActionForCommand<DeleteCertificatesCommand>(coll->action(QStringLiteral("certificates_delete")));
//---
registerActionForCommand<RefreshCertificatesCommand>(coll->action(QStringLiteral("certificates_refresh")));
//---
registerActionForCommand<DumpCertificateCommand>(coll->action(QStringLiteral("certificates_dump_certificate")));
registerActionForCommand<RefreshX509CertsCommand>(coll->action(QStringLiteral("tools_refresh_x509_certificates")));
registerActionForCommand<RefreshOpenPGPCertsCommand>(coll->action(QStringLiteral("tools_refresh_openpgp_certificates")));
//---
registerActionForCommand<ImportCrlCommand>(coll->action(QStringLiteral("crl_import_crl")));
//---
registerActionForCommand<ClearCrlCacheCommand>(coll->action(QStringLiteral("crl_clear_crl_cache")));
registerActionForCommand<DumpCrlCacheCommand>(coll->action(QStringLiteral("crl_dump_crl_cache")));
enableDisableActions(nullptr);
}
void KeyListController::registerAction(QAction *action, Command::Restrictions restrictions, Command *(*create)(QAbstractItemView *, KeyListController *))
{
if (!action) {
return;
}
Q_ASSERT(!action->isCheckable()); // can be added later, for now, disallow
const Private::action_item ai = {action, restrictions, create};
connect(action, &QAction::triggered, this, [this, action]() {
d->slotActionTriggered(action);
});
d->actions.push_back(ai);
}
void KeyListController::registerCommand(Command *cmd)
{
if (!cmd || ranges::binary_search(d->commands, cmd)) {
return;
}
d->addCommand(cmd);
qCDebug(KLEOPATRA_LOG) << (void *)cmd;
if (d->commands.size() == 1) {
Q_EMIT commandsExecuting(true);
}
}
bool KeyListController::hasRunningCommands() const
{
return !d->commands.empty();
}
bool KeyListController::shutdownWarningRequired() const
{
return ranges::any_of(d->commands, std::mem_fn(&Command::warnWhenRunningAtShutdown));
}
// slot
void KeyListController::cancelCommands()
{
ranges::for_each(d->commands, std::mem_fn(&Command::cancel));
}
void KeyListController::Private::connectView(QAbstractItemView *view)
{
connect(view, &QObject::destroyed, q, [this](QObject *obj) {
slotDestroyed(obj);
});
connect(view, &QAbstractItemView::doubleClicked, q, [this](const QModelIndex &index) {
slotDoubleClicked(index);
});
connect(view, &QAbstractItemView::activated, q, [this](const QModelIndex &index) {
slotActivated(index);
});
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, q, [this](const QItemSelection &oldSel, const QItemSelection &newSel) {
slotSelectionChanged(oldSel, newSel);
});
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
slotContextMenu(pos);
});
}
void KeyListController::Private::connectCommand(Command *cmd)
{
if (!cmd) {
return;
}
connect(cmd, &QObject::destroyed, q, [this](QObject *obj) {
slotDestroyed(obj);
});
connect(cmd, &Command::finished, q, [this] {
slotCommandFinished();
});
// connect( cmd, SIGNAL(canceled()), q, SLOT(slotCommandCanceled()) );
connect(cmd, &Command::progress, q, &KeyListController::progress);
}
void KeyListController::Private::slotDoubleClicked(const QModelIndex &idx)
{
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (!view || !ranges::binary_search(views, view)) {
return;
}
if (const auto *const keyListModel = dynamic_cast<KeyListModelInterface *>(view->model())) {
DetailsCommand *const c = new DetailsCommand{keyListModel->key(idx)};
c->setParentWidget(parentWidget ? parentWidget : view);
c->start();
}
}
void KeyListController::Private::slotActivated(const QModelIndex &idx)
{
Q_UNUSED(idx)
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (!view || !ranges::binary_search(views, view)) {
return;
}
}
void KeyListController::Private::slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_)
{
Q_UNUSED(old)
Q_UNUSED(new_)
const QItemSelectionModel *const sm = qobject_cast<QItemSelectionModel *>(q->sender());
if (!sm) {
return;
}
q->enableDisableActions(sm);
}
void KeyListController::Private::slotContextMenu(const QPoint &p)
{
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (view && ranges::binary_search(views, view)) {
Q_EMIT q->contextMenuRequested(view, view->viewport()->mapToGlobal(p));
} else {
qCDebug(KLEOPATRA_LOG) << "sender is not a QAbstractItemView*!";
}
}
void KeyListController::Private::slotCommandFinished()
{
Command *const cmd = qobject_cast<Command *>(q->sender());
if (!cmd || !ranges::binary_search(commands, cmd)) {
return;
}
qCDebug(KLEOPATRA_LOG) << (void *)cmd;
if (commands.size() == 1) {
Q_EMIT q->commandsExecuting(false);
}
}
void KeyListController::enableDisableActions(const QItemSelectionModel *sm) const
{
const Command::Restrictions restrictionsMask = d->calculateRestrictionsMask(sm);
for (const Private::action_item &ai : std::as_const(d->actions))
if (ai.action) {
ai.action->setEnabled(ai.restrictions == (ai.restrictions & restrictionsMask));
}
}
static bool all_secret_are_not_owner_trust_ultimate(const std::vector<Key> &keys)
{
for (const Key &key : keys)
if (key.hasSecret() && key.ownerTrust() == Key::Ultimate) {
return false;
}
return true;
}
Command::Restrictions find_root_restrictions(const std::vector<Key> &keys)
{
bool trusted = false, untrusted = false;
for (const Key &key : keys)
if (key.isRoot())
if (key.userID(0).validity() == UserID::Ultimate) {
trusted = true;
} else {
untrusted = true;
}
else {
return Command::NoRestriction;
}
if (trusted)
if (untrusted) {
return Command::NoRestriction;
} else {
return Command::MustBeTrustedRoot;
}
else if (untrusted) {
return Command::MustBeUntrustedRoot;
} else {
return Command::NoRestriction;
}
}
static bool secretSubkeyDataAvailable(const Subkey &subkey)
{
return subkey.isSecret() && !subkey.isCardKey();
};
Command::Restrictions KeyListController::Private::calculateRestrictionsMask(const QItemSelectionModel *sm)
{
if (!sm) {
return Command::NoRestriction;
}
const KeyListModelInterface *const m = dynamic_cast<const KeyListModelInterface *>(sm->model());
if (!m) {
return Command::NoRestriction;
}
const std::vector<Key> keys = m->keys(sm->selectedRows());
if (keys.empty()) {
return Command::NoRestriction;
}
Command::Restrictions result = Command::NeedSelection;
if (keys.size() == 1) {
result |= Command::OnlyOneKey;
}
// we need to check the primary subkey because Key::hasSecret() is also true if just the secret key stub of an offline key is available
const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) {
return k.subkey(0).isSecret();
};
if (ranges::all_of(keys, primaryKeyCanBeUsedForSecretKeyOperations)) {
result |= Command::NeedSecretKey;
}
if (ranges::all_of(keys, [](const auto &k) {
return ranges::any_of(k.subkeys(), &secretSubkeyDataAvailable);
})) {
result |= Command::NeedSecretSubkeyData;
}
if ((result & Command::NeedSecretSubkeyData) && ranges::all_of(keys, [](const auto &k) {
return secretSubkeyDataAvailable(k.subkey(0));
})) {
result |= Command::NeedSecretPrimaryKeyData;
}
if (ranges::all_of(keys, [](const Key &key) {
return key.protocol() == OpenPGP;
})) {
result |= Command::MustBeOpenPGP;
} else if (ranges::all_of(keys, [](const Key &key) {
return key.protocol() == CMS;
})) {
result |= Command::MustBeCMS;
}
if (ranges::all_of(keys, [](const auto &key) {
return !key.isBad();
})) {
result |= Command::MustBeValid;
}
if (all_secret_are_not_owner_trust_ultimate(keys)) {
result |= Command::MayOnlyBeSecretKeyIfOwnerTrustIsNotYetUltimate;
}
result |= find_root_restrictions(keys);
if (const ReaderStatus *rs = ReaderStatus::instance()) {
if (!rs->firstCardWithNullPin().empty()) {
result |= Command::AnyCardHasNullPin;
}
}
+ {
+ if (keys.size() > 0) {
+ bool hasSignCertify = false;
+ bool hasEncrypt = false;
+ bool hasAuthenticate = false;
+ bool invalid = false;
+ for (const auto &subkey : keys[0].subkeys()) {
+ if (subkey.isCardKey()) {
+ invalid = true;
+ }
+ if (subkey.canCertify() && subkey.canSign()) {
+ if (hasSignCertify) {
+ invalid = true;
+ break;
+ } else {
+ hasSignCertify = true;
+ }
+ } else if (subkey.canEncrypt()) {
+ if (hasEncrypt) {
+ invalid = true;
+ break;
+ } else {
+ hasEncrypt = true;
+ }
+ } else if (subkey.canAuthenticate()) {
+ if (hasAuthenticate) {
+ invalid = true;
+ break;
+ } else {
+ hasAuthenticate = true;
+ }
+ } else {
+ invalid = true;
+ break;
+ }
+ }
+ if (hasSignCertify && hasEncrypt && !invalid) {
+ result |= Command::SuitableForCard;
+ }
+ }
+ }
+
return result;
}
void KeyListController::Private::slotActionTriggered(QAction *sender)
{
const auto it = ranges::find_if(actions, [sender](const action_item &item) {
return item.action == sender;
});
if (it != actions.end())
if (Command *const c = it->createCommand(this->currentView, q)) {
if (parentWidget) {
c->setParentWidget(parentWidget);
}
c->start();
} else
qCDebug(KLEOPATRA_LOG) << "createCommand() == NULL for action(?) \"" << qPrintable(sender->objectName()) << "\"";
else {
qCDebug(KLEOPATRA_LOG) << "I don't know anything about action(?) \"%s\"", qPrintable(sender->objectName());
}
}
int KeyListController::Private::toolTipOptions() const
{
using namespace Kleo::Formatting;
static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
static const int ownerFlags = Subject | UserIDs | OwnerTrust;
static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;
const TooltipPreferences prefs;
int flags = KeyID;
flags |= prefs.showValidity() ? validityFlags : 0;
flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
return flags;
}
void KeyListController::updateConfig()
{
const int opts = d->toolTipOptions();
if (d->flatModel) {
d->flatModel->setToolTipOptions(opts);
}
if (d->hierarchicalModel) {
d->hierarchicalModel->setToolTipOptions(opts);
}
}
#include "moc_keylistcontroller.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jan 12, 11:43 PM (1 d, 8 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
65/fa/e85f78c325ccd2ad59f6a2f85fe0

Event Timeline