Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34504790
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
127 KB
Subscribers
None
View Options
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>&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>&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>&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>&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>&Settings</text>
<Action name="settings_self_test"/>
<Action name="configure_groups" append="configure_merge"/>
</Menu>
<Menu name="window" append="settings_merge">
<text>&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>&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>&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>&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
Details
Attached
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
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment