Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index be87b09ef..2102b9670 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,626 +1,626 @@
# 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/refreshcertificatecommand.cpp
commands/refreshcertificatecommand.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/certificatedetailsdialog.cpp
dialogs/certificatedetailsdialog.h
dialogs/certificatedetailsinputwidget.cpp
dialogs/certificatedetailsinputwidget.h
dialogs/certificatedetailswidget.cpp
dialogs/certificatedetailswidget.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/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/weboftrustdialog.cpp
- dialogs/weboftrustdialog.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/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/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/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
dialogs/trustchainwidget.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/dialogs/certificatedetailsdialog.cpp b/src/dialogs/certificatedetailsdialog.cpp
index 5c82d1568..b41290a3d 100644
--- a/src/dialogs/certificatedetailsdialog.cpp
+++ b/src/dialogs/certificatedetailsdialog.cpp
@@ -1,83 +1,213 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatedetailsdialog.h"
#include "certificatedetailswidget.h"
+#include "commands/changepassphrasecommand.h"
+#include "commands/dumpcertificatecommand.h"
+#include "commands/genrevokecommand.h"
+#include "commands/refreshcertificatecommand.h"
+#include "exportdialog.h"
+
+#include <Libkleo/KeyHelpers>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <gpgme++/key.h>
+using namespace Kleo;
+
+class CertificateDetailsDialog::Private
+{
+public:
+ Private(CertificateDetailsDialog *qq)
+ : q(qq)
+ {
+ }
+ QPushButton *changePassphraseBtn = nullptr;
+ QPushButton *genRevokeBtn = nullptr;
+ QPushButton *exportBtn = nullptr;
+ QPushButton *refreshBtn = nullptr;
+ QPushButton *moreDetailsBtn = nullptr;
+ void refreshCertificate();
+ void exportClicked();
+ void genRevokeCert();
+ void changePassphrase();
+ void showMoreDetails();
+
+private:
+ CertificateDetailsDialog *q;
+};
+
CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent)
: QDialog(parent)
+ , d(new Private(this))
{
auto l = new QVBoxLayout(this);
auto w = new CertificateDetailsWidget{this};
w->layout()->setContentsMargins(0, 0, 0, 0);
l->addWidget(w);
auto bbox = new QDialogButtonBox(this);
- auto btn = bbox->addButton(QDialogButtonBox::Close);
- connect(btn, &QPushButton::pressed, this, &QDialog::accept);
+
+ d->refreshBtn = new QPushButton{i18nc("@action:button", "Update"), parent};
+ bbox->addButton(d->refreshBtn, QDialogButtonBox::ActionRole);
+
+ d->exportBtn = new QPushButton(i18nc("@action:button", "Export"), parent);
+ bbox->addButton(d->exportBtn, QDialogButtonBox::ActionRole);
+
+ d->genRevokeBtn = new QPushButton(i18nc("@action:button", "Generate Revocation Certificate"), parent);
+ d->genRevokeBtn->setToolTip(u"<html>"
+ % i18n("A revocation certificate is a file that serves as a \"kill switch\" to publicly "
+ "declare that a key shall not anymore be used. It is not possible "
+ "to retract such a revocation certificate once it has been published.")
+ % u"</html>");
+ bbox->addButton(d->genRevokeBtn, QDialogButtonBox::ActionRole);
+
+ d->moreDetailsBtn = new QPushButton(i18nc("@action:button", "More Details..."), parent);
+ bbox->addButton(d->moreDetailsBtn, QDialogButtonBox::ActionRole);
+
+ d->changePassphraseBtn = new QPushButton(i18nc("@action:button", "Change Passphrase"), parent);
+ bbox->addButton(d->changePassphraseBtn, QDialogButtonBox::ActionRole);
+
+ auto closeBtn = bbox->addButton(QDialogButtonBox::Close);
+ connect(closeBtn, &QPushButton::pressed, this, &QDialog::accept);
l->addWidget(bbox);
+
+ connect(d->exportBtn, &QPushButton::clicked, this, [this]() {
+ d->exportClicked();
+ });
+
+ connect(d->refreshBtn, &QPushButton::clicked, this, [this]() {
+ d->refreshCertificate();
+ });
+ connect(d->genRevokeBtn, &QPushButton::clicked, this, [this]() {
+ d->genRevokeCert();
+ });
+ connect(d->changePassphraseBtn, &QPushButton::clicked, this, [this]() {
+ d->changePassphrase();
+ });
+ connect(d->moreDetailsBtn, &QPushButton::pressed, this, [this]() {
+ d->showMoreDetails();
+ });
readConfig();
}
CertificateDetailsDialog::~CertificateDetailsDialog()
{
writeConfig();
}
void CertificateDetailsDialog::readConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("CertificateDetailsDialog"));
const QSize size = dialog.readEntry("Size", QSize(730, 280));
if (size.isValid()) {
resize(size);
}
}
void CertificateDetailsDialog::writeConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("CertificateDetailsDialog"));
dialog.writeEntry("Size", size());
dialog.sync();
}
namespace
{
QString title(const GpgME::Key &key)
{
switch (key.protocol()) {
case GpgME::OpenPGP:
return i18nc("@title:window", "OpenPGP Certificate");
case GpgME::CMS:
return i18nc("@title:window", "S/MIME Certificate");
default:
return {};
}
}
}
void CertificateDetailsDialog::setKey(const GpgME::Key &key)
{
setWindowTitle(title(key));
findChild<CertificateDetailsWidget *>()->setKey(key);
+ d->exportBtn->setVisible(!isRemoteKey(key));
+ d->refreshBtn->setVisible(!isRemoteKey(key));
+
+ if (key.protocol() == GpgME::Protocol::CMS) {
+ d->refreshBtn->setToolTip(i18nc("@info:tooltip", "Update the CRLs and do a full validation check of the certificate."));
+ } else {
+ d->refreshBtn->setToolTip(i18nc("@info:tooltip", "Update the key from external sources."));
+ }
+ d->genRevokeBtn->setVisible(key.protocol() == GpgME::Protocol::OpenPGP && key.hasSecret());
+ d->genRevokeBtn->setEnabled(canBeUsedForSecretKeyOperations(key));
+ d->changePassphraseBtn->setVisible(isSecretKeyStoredInKeyRing(key));
+ d->moreDetailsBtn->setVisible(!isRemoteKey(key) && key.protocol() == GpgME::Protocol::CMS);
+ d->moreDetailsBtn->setText(i18nc("@action:button", "More Details"));
}
GpgME::Key CertificateDetailsDialog::key() const
{
return findChild<CertificateDetailsWidget *>()->key();
}
+void CertificateDetailsDialog::Private::exportClicked()
+{
+ QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
+ dlg->setKey(q->key());
+ dlg->exec();
+}
+
+void CertificateDetailsDialog::Private::refreshCertificate()
+{
+ auto cmd = new Kleo::RefreshCertificateCommand(q->key());
+ QObject::connect(cmd, &Kleo::RefreshCertificateCommand::finished, q, [this]() {
+ refreshBtn->setEnabled(true);
+ });
+ refreshBtn->setEnabled(false);
+ cmd->start();
+}
+
+void CertificateDetailsDialog::Private::genRevokeCert()
+{
+ auto cmd = new Kleo::Commands::GenRevokeCommand(q->key());
+ QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished, q, [this]() {
+ genRevokeBtn->setEnabled(true);
+ });
+ genRevokeBtn->setEnabled(false);
+ cmd->start();
+}
+
+void CertificateDetailsDialog::Private::changePassphrase()
+{
+ auto cmd = new Kleo::Commands::ChangePassphraseCommand(q->key());
+ QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished, q, [this]() {
+ changePassphraseBtn->setEnabled(true);
+ });
+ changePassphraseBtn->setEnabled(false);
+ cmd->start();
+}
+
+void CertificateDetailsDialog::Private::showMoreDetails()
+{
+ if (q->key().protocol() == GpgME::CMS) {
+ auto cmd = new Kleo::Commands::DumpCertificateCommand(q->key());
+ cmd->setParentWidget(q);
+ cmd->setUseDialog(true);
+ cmd->start();
+ }
+}
+
#include "moc_certificatedetailsdialog.cpp"
diff --git a/src/dialogs/certificatedetailsdialog.h b/src/dialogs/certificatedetailsdialog.h
index beea7d067..c00dc72a6 100644
--- a/src/dialogs/certificatedetailsdialog.h
+++ b/src/dialogs/certificatedetailsdialog.h
@@ -1,28 +1,31 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QDialog>
namespace GpgME
{
class Key;
}
class CertificateDetailsDialog : public QDialog
{
Q_OBJECT
public:
explicit CertificateDetailsDialog(QWidget *parent = nullptr);
~CertificateDetailsDialog() override;
void setKey(const GpgME::Key &key);
GpgME::Key key() const;
private:
void readConfig();
void writeConfig();
+
+ class Private;
+ std::unique_ptr<Private> d;
};
diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp
index 643735ad1..5517bde40 100644
--- a/src/dialogs/certificatedetailswidget.cpp
+++ b/src/dialogs/certificatedetailswidget.cpp
@@ -1,1137 +1,655 @@
/*
dialogs/certificatedetailswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-FileCopyrightText: 2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certificatedetailswidget.h"
-#include "exportdialog.h"
+#include "dialogs/weboftrustwidget.h"
#include "kleopatra_debug.h"
#include "subkeyswidget.h"
#include "trustchainwidget.h"
-#include "weboftrustdialog.h"
+#include "useridswidget.h"
-#include "commands/certifycertificatecommand.h"
#include "commands/changeexpirycommand.h"
-#include "commands/changepassphrasecommand.h"
-#ifdef MAILAKONADI_ENABLED
-#include "commands/exportopenpgpcerttoprovidercommand.h"
-#endif // MAILAKONADI_ENABLED
-#include "commands/adduseridcommand.h"
#include "commands/detailscommand.h"
-#include "commands/dumpcertificatecommand.h"
-#include "commands/genrevokecommand.h"
-#include "commands/refreshcertificatecommand.h"
-#include "commands/revokecertificationcommand.h"
-#include "commands/revokeuseridcommand.h"
-#include "commands/setprimaryuseridcommand.h"
#include "utils/accessibility.h"
#include "utils/tags.h"
#include "view/infofield.h"
#include <Libkleo/Algorithm>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/TreeWidget>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
-#include <gpgme++/tofuinfo.h>
#include <QGpgME/Debug>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <QClipboard>
#include <QDateTime>
#include <QGridLayout>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QLocale>
#include <QMenu>
#include <QPushButton>
#include <QStringBuilder>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <map>
#if __has_include(<ranges>)
#include <ranges>
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
#define USE_RANGES
#endif
#endif
#include <set>
Q_DECLARE_METATYPE(GpgME::UserID)
using namespace Kleo;
-namespace
-{
-std::vector<GpgME::UserID> selectedUserIDs(const QTreeWidget *treeWidget)
-{
- if (!treeWidget) {
- return {};
- }
-
- std::vector<GpgME::UserID> userIDs;
- const auto selected = treeWidget->selectedItems();
- std::transform(selected.begin(), selected.end(), std::back_inserter(userIDs), [](const QTreeWidgetItem *item) {
- return item->data(0, Qt::UserRole).value<GpgME::UserID>();
- });
- return userIDs;
-}
-}
-
class CertificateDetailsWidget::Private
{
public:
Private(CertificateDetailsWidget *qq);
void setupCommonProperties();
- void updateUserIDActions();
- void setUpUserIDTable();
void setUpSMIMEAdressList();
void setupPGPProperties();
void setupSMIMEProperties();
- void revokeUserID(const GpgME::UserID &uid);
- void revokeSelectedUserID();
- void genRevokeCert();
void refreshCertificate();
- void certifyUserIDs();
- void revokeCertifications();
- void webOfTrustClicked();
- void exportClicked();
- void addUserID();
- void setPrimaryUserID(const GpgME::UserID &uid = {});
- void changePassphrase();
void changeExpiration();
void keysMayHaveChanged();
- void showTrustChainDialog();
- void showMoreDetails();
- void userIDTableContextMenuRequested(const QPoint &p);
- QString tofuTooltipString(const GpgME::UserID &uid) const;
QIcon trustLevelIcon(const GpgME::UserID &uid) const;
QString trustLevelText(const GpgME::UserID &uid) const;
void showIssuerCertificate();
void updateKey();
void setUpdatedKey(const GpgME::Key &key);
void keyListDone(const GpgME::KeyListResult &, const std::vector<GpgME::Key> &, const QString &, const GpgME::Error &);
void copyFingerprintToClipboard();
+ void setUpdateInProgress(bool updateInProgress);
private:
CertificateDetailsWidget *const q;
public:
GpgME::Key key;
bool updateInProgress = false;
private:
InfoField *attributeField(const QString &attributeName)
{
const auto keyValuePairIt = ui.smimeAttributeFields.find(attributeName);
if (keyValuePairIt != ui.smimeAttributeFields.end()) {
return (*keyValuePairIt).second.get();
}
return nullptr;
}
private:
struct UI {
- QWidget *userIDs = nullptr;
- QLabel *userIDTableLabel = nullptr;
- TreeWidget *userIDTable = nullptr;
- QPushButton *addUserIDBtn = nullptr;
- QPushButton *setPrimaryUserIDBtn = nullptr;
- QPushButton *certifyBtn = nullptr;
- QPushButton *revokeCertificationsBtn = nullptr;
- QPushButton *revokeUserIDBtn = nullptr;
- QPushButton *webOfTrustBtn = nullptr;
+ UserIdsWidget *userIDs = nullptr;
std::map<QString, std::unique_ptr<InfoField>> smimeAttributeFields;
std::unique_ptr<InfoField> smimeTrustLevelField;
std::unique_ptr<InfoField> validFromField;
std::unique_ptr<InfoField> expiresField;
QAction *changeExpirationAction = nullptr;
std::unique_ptr<InfoField> fingerprintField;
QAction *copyFingerprintAction = nullptr;
std::unique_ptr<InfoField> smimeIssuerField;
QAction *showIssuerCertificateAction = nullptr;
std::unique_ptr<InfoField> complianceField;
std::unique_ptr<InfoField> trustedIntroducerField;
+ std::unique_ptr<InfoField> primaryUserIdField;
+ std::unique_ptr<InfoField> privateKeyInfoField;
- QLabel *smimeRelatedAddresses = nullptr;
QListWidget *smimeAddressList = nullptr;
- QPushButton *moreDetailsBtn = nullptr;
- QPushButton *trustChainDetailsBtn = nullptr;
- QPushButton *refreshBtn = nullptr;
- QPushButton *changePassphraseBtn = nullptr;
- QPushButton *exportBtn = nullptr;
- QPushButton *genRevokeBtn = nullptr;
+ QTabWidget *tabWidget = nullptr;
+ SubKeysWidget *subKeysWidget = nullptr;
+ WebOfTrustWidget *webOfTrustWidget = nullptr;
+ TrustChainWidget *trustChainWidget = nullptr;
void setupUi(QWidget *parent)
{
auto mainLayout = new QVBoxLayout{parent};
- userIDs = new QWidget{parent};
- {
- auto userIDsLayout = new QVBoxLayout{userIDs};
- userIDsLayout->setContentsMargins({});
-
- userIDTableLabel = new QLabel(i18n("User IDs:"), parent);
- userIDsLayout->addWidget(userIDTableLabel);
-
- userIDTable = new TreeWidget{parent};
- userIDTableLabel->setBuddy(userIDTable);
- userIDTable->setAccessibleName(i18n("User IDs"));
- QTreeWidgetItem *__qtreewidgetitem = new QTreeWidgetItem();
- __qtreewidgetitem->setText(0, QString::fromUtf8("1"));
- userIDTable->setHeaderItem(__qtreewidgetitem);
- userIDTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- userIDTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- userIDTable->setRootIsDecorated(false);
- userIDTable->setUniformRowHeights(true);
- userIDTable->setAllColumnsShowFocus(false);
-
- userIDsLayout->addWidget(userIDTable);
-
- {
- auto buttonRow = new QHBoxLayout;
-
- addUserIDBtn = new QPushButton(i18nc("@action:button", "Add User ID"), parent);
- buttonRow->addWidget(addUserIDBtn);
-
- setPrimaryUserIDBtn = new QPushButton{i18nc("@action:button", "Flag as Primary"), parent};
- setPrimaryUserIDBtn->setToolTip(i18nc("@info:tooltip", "Flag the selected user ID as the primary user ID of this key."));
- buttonRow->addWidget(setPrimaryUserIDBtn);
-
- certifyBtn = new QPushButton(i18nc("@action:button", "Certify User IDs"), parent);
- buttonRow->addWidget(certifyBtn);
-
- webOfTrustBtn = new QPushButton(i18nc("@action:button", "Show Certifications"), parent);
- buttonRow->addWidget(webOfTrustBtn);
-
- revokeCertificationsBtn = new QPushButton(i18nc("@action:button", "Revoke Certifications"), parent);
- buttonRow->addWidget(revokeCertificationsBtn);
-
- revokeUserIDBtn = new QPushButton(i18nc("@action:button", "Revoke User ID"), parent);
- buttonRow->addWidget(revokeUserIDBtn);
-
- buttonRow->addStretch(1);
-
- userIDsLayout->addLayout(buttonRow);
- }
-
- userIDsLayout->addWidget(new KSeparator{Qt::Horizontal, parent});
- }
-
- mainLayout->addWidget(userIDs);
-
{
auto gridLayout = new QGridLayout;
gridLayout->setColumnStretch(1, 1);
int row = -1;
for (const auto &attribute : DN::attributeOrder()) {
const auto attributeLabel = DN::attributeNameToLabel(attribute);
if (attributeLabel.isEmpty()) {
continue;
}
const auto labelWithColon = i18nc("interpunctation for labels", "%1:", attributeLabel);
const auto &[it, inserted] = smimeAttributeFields.try_emplace(attribute, std::make_unique<InfoField>(labelWithColon, parent));
if (inserted) {
row++;
const auto &field = it->second;
gridLayout->addWidget(field->label(), row, 0);
gridLayout->addLayout(field->layout(), row, 1);
}
}
row++;
smimeTrustLevelField = std::make_unique<InfoField>(i18n("Trust level:"), parent);
gridLayout->addWidget(smimeTrustLevelField->label(), row, 0);
gridLayout->addLayout(smimeTrustLevelField->layout(), row, 1);
row++;
validFromField = std::make_unique<InfoField>(i18n("Valid from:"), parent);
gridLayout->addWidget(validFromField->label(), row, 0);
gridLayout->addLayout(validFromField->layout(), row, 1);
row++;
expiresField = std::make_unique<InfoField>(i18n("Valid until:"), parent);
changeExpirationAction = new QAction{parent};
changeExpirationAction->setIcon(QIcon::fromTheme(QStringLiteral("editor")));
changeExpirationAction->setToolTip(i18nc("@info:tooltip", "Change the end of the validity period"));
Kleo::setAccessibleName(changeExpirationAction, i18nc("@action:button", "Change Validity"));
expiresField->setAction(changeExpirationAction);
gridLayout->addWidget(expiresField->label(), row, 0);
gridLayout->addLayout(expiresField->layout(), row, 1);
row++;
fingerprintField = std::make_unique<InfoField>(i18n("Fingerprint:"), parent);
if (QGuiApplication::clipboard()) {
copyFingerprintAction = new QAction{parent};
copyFingerprintAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
copyFingerprintAction->setToolTip(i18nc("@info:tooltip", "Copy the fingerprint to the clipboard"));
Kleo::setAccessibleName(copyFingerprintAction, i18nc("@action:button", "Copy fingerprint"));
fingerprintField->setAction(copyFingerprintAction);
}
gridLayout->addWidget(fingerprintField->label(), row, 0);
gridLayout->addLayout(fingerprintField->layout(), row, 1);
row++;
smimeIssuerField = std::make_unique<InfoField>(i18n("Issuer:"), parent);
showIssuerCertificateAction = new QAction{parent};
showIssuerCertificateAction->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
showIssuerCertificateAction->setToolTip(i18nc("@info:tooltip", "Show the issuer certificate"));
Kleo::setAccessibleName(showIssuerCertificateAction, i18nc("@action:button", "Show certificate"));
smimeIssuerField->setAction(showIssuerCertificateAction);
gridLayout->addWidget(smimeIssuerField->label(), row, 0);
gridLayout->addLayout(smimeIssuerField->layout(), row, 1);
row++;
complianceField = std::make_unique<InfoField>(i18n("Compliance:"), parent);
gridLayout->addWidget(complianceField->label(), row, 0);
gridLayout->addLayout(complianceField->layout(), row, 1);
row++;
trustedIntroducerField = std::make_unique<InfoField>(i18n("Trusted introducer for:"), parent);
gridLayout->addWidget(trustedIntroducerField->label(), row, 0);
trustedIntroducerField->setToolTip(i18n("See certifications for details."));
gridLayout->addLayout(trustedIntroducerField->layout(), row, 1);
+ row++;
+ privateKeyInfoField = std::make_unique<InfoField>(i18n("Private Key:"), parent);
+ gridLayout->addWidget(privateKeyInfoField->label(), row, 0);
+ gridLayout->addLayout(privateKeyInfoField->layout(), row, 1);
+ privateKeyInfoField->setValue(i18n("Smartcard"));
+
+ row++;
+ primaryUserIdField = std::make_unique<InfoField>(i18n("User ID:"), parent);
+ gridLayout->addWidget(primaryUserIdField->label(), row, 0);
+ gridLayout->addLayout(primaryUserIdField->layout(), row, 1);
+
mainLayout->addLayout(gridLayout);
}
- smimeRelatedAddresses = new QLabel(i18n("Related addresses:"), parent);
- mainLayout->addWidget(smimeRelatedAddresses);
+ tabWidget = new QTabWidget(parent);
+
+ mainLayout->addWidget(tabWidget);
+ userIDs = new UserIdsWidget(parent);
+
+ tabWidget->addTab(userIDs, i18n("User IDs"));
smimeAddressList = new QListWidget{parent};
- smimeRelatedAddresses->setBuddy(smimeAddressList);
smimeAddressList->setAccessibleName(i18n("Related addresses"));
smimeAddressList->setEditTriggers(QAbstractItemView::NoEditTriggers);
smimeAddressList->setSelectionMode(QAbstractItemView::SingleSelection);
+ tabWidget->addTab(smimeAddressList, i18n("Related addresses"));
- mainLayout->addWidget(smimeAddressList);
-
- mainLayout->addStretch();
+ subKeysWidget = new SubKeysWidget(parent);
+ tabWidget->addTab(subKeysWidget, i18n("Subkeys"));
- {
- auto buttonRow = new QHBoxLayout;
-
- moreDetailsBtn = new QPushButton(i18nc("@action:button", "More Details..."), parent);
- buttonRow->addWidget(moreDetailsBtn);
-
- trustChainDetailsBtn = new QPushButton(i18nc("@action:button", "Trust Chain Details"), parent);
- buttonRow->addWidget(trustChainDetailsBtn);
-
- refreshBtn = new QPushButton{i18nc("@action:button", "Update"), parent};
- buttonRow->addWidget(refreshBtn);
-
- exportBtn = new QPushButton(i18nc("@action:button", "Export"), parent);
- buttonRow->addWidget(exportBtn);
-
- changePassphraseBtn = new QPushButton(i18nc("@action:button", "Change Passphrase"), parent);
- buttonRow->addWidget(changePassphraseBtn);
+ webOfTrustWidget = new WebOfTrustWidget(parent);
+ tabWidget->addTab(webOfTrustWidget, i18n("Certifications"));
- genRevokeBtn = new QPushButton(i18nc("@action:button", "Generate Revocation Certificate"), parent);
- genRevokeBtn->setToolTip(u"<html>"
- % i18n("A revocation certificate is a file that serves as a \"kill switch\" to publicly "
- "declare that a key shall not anymore be used. It is not possible "
- "to retract such a revocation certificate once it has been published.")
- % u"</html>");
- buttonRow->addWidget(genRevokeBtn);
-
- buttonRow->addStretch(1);
-
- mainLayout->addLayout(buttonRow);
- }
+ trustChainWidget = new TrustChainWidget(parent);
+ tabWidget->addTab(trustChainWidget, i18n("Trust chain details"));
}
} ui;
};
CertificateDetailsWidget::Private::Private(CertificateDetailsWidget *qq)
: q{qq}
{
ui.setupUi(q);
- ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested, q, [this](const QPoint &p) {
- userIDTableContextMenuRequested(p);
- });
- connect(ui.userIDTable, &QTreeWidget::itemSelectionChanged, q, [this]() {
- updateUserIDActions();
- });
- connect(ui.addUserIDBtn, &QPushButton::clicked, q, [this]() {
- addUserID();
- });
- connect(ui.setPrimaryUserIDBtn, &QPushButton::clicked, q, [this]() {
- setPrimaryUserID();
- });
- connect(ui.revokeUserIDBtn, &QPushButton::clicked, q, [this]() {
- revokeSelectedUserID();
- });
- connect(ui.changePassphraseBtn, &QPushButton::clicked, q, [this]() {
- changePassphrase();
- });
- connect(ui.genRevokeBtn, &QPushButton::clicked, q, [this]() {
- genRevokeCert();
- });
connect(ui.changeExpirationAction, &QAction::triggered, q, [this]() {
changeExpiration();
});
connect(ui.showIssuerCertificateAction, &QAction::triggered, q, [this]() {
showIssuerCertificate();
});
- connect(ui.trustChainDetailsBtn, &QPushButton::pressed, q, [this]() {
- showTrustChainDialog();
- });
- connect(ui.moreDetailsBtn, &QPushButton::pressed, q, [this]() {
- showMoreDetails();
- });
- connect(ui.refreshBtn, &QPushButton::clicked, q, [this]() {
- refreshCertificate();
- });
- connect(ui.certifyBtn, &QPushButton::clicked, q, [this]() {
- certifyUserIDs();
- });
- connect(ui.revokeCertificationsBtn, &QPushButton::clicked, q, [this]() {
- revokeCertifications();
- });
- connect(ui.webOfTrustBtn, &QPushButton::clicked, q, [this]() {
- webOfTrustClicked();
- });
- connect(ui.exportBtn, &QPushButton::clicked, q, [this]() {
- exportClicked();
- });
if (ui.copyFingerprintAction) {
connect(ui.copyFingerprintAction, &QAction::triggered, q, [this]() {
copyFingerprintToClipboard();
});
}
connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, q, [this]() {
keysMayHaveChanged();
});
+ connect(ui.userIDs, &UserIdsWidget::updateKey, q, [this]() {
+ updateKey();
+ });
}
void CertificateDetailsWidget::Private::setupCommonProperties()
{
const bool isOpenPGP = key.protocol() == GpgME::OpenPGP;
const bool isSMIME = key.protocol() == GpgME::CMS;
const bool isOwnKey = key.hasSecret();
- const auto isLocalKey = !isRemoteKey(key);
- const auto keyCanBeCertified = Kleo::canBeCertified(key);
-
- // update visibility of UI elements
- ui.userIDs->setVisible(isOpenPGP);
- ui.addUserIDBtn->setVisible(isOwnKey);
- ui.setPrimaryUserIDBtn->setVisible(isOwnKey);
- // ui.certifyBtn->setVisible(true); // always visible (for OpenPGP keys)
- // ui.webOfTrustBtn->setVisible(true); // always visible (for OpenPGP keys)
- ui.revokeCertificationsBtn->setVisible(Kleo::Commands::RevokeCertificationCommand::isSupported());
- ui.revokeUserIDBtn->setVisible(isOwnKey);
for (const auto &[_, field] : ui.smimeAttributeFields) {
field->setVisible(isSMIME);
}
ui.smimeTrustLevelField->setVisible(isSMIME);
// ui.validFromField->setVisible(true); // always visible
// ui.expiresField->setVisible(true); // always visible
if (isOpenPGP && isOwnKey) {
ui.expiresField->setAction(ui.changeExpirationAction);
} else {
ui.expiresField->setAction(nullptr);
}
// ui.fingerprintField->setVisible(true); // always visible
ui.smimeIssuerField->setVisible(isSMIME);
ui.complianceField->setVisible(DeVSCompliance::isCompliant());
ui.trustedIntroducerField->setVisible(isOpenPGP); // may be hidden again by setupPGPProperties()
- ui.smimeRelatedAddresses->setVisible(isSMIME);
- ui.smimeAddressList->setVisible(isSMIME);
-
- ui.moreDetailsBtn->setVisible(isLocalKey);
- ui.moreDetailsBtn->setText(isSMIME ? i18nc("@action:button", "More Details...")
- : isOwnKey ? i18nc("@action:button", "Manage Subkeys")
- : i18nc("@action:button", "Show Subkeys"));
-
- ui.trustChainDetailsBtn->setVisible(isSMIME);
- ui.refreshBtn->setVisible(isLocalKey);
- ui.changePassphraseBtn->setVisible(isSecretKeyStoredInKeyRing(key));
- ui.exportBtn->setVisible(isLocalKey);
- ui.genRevokeBtn->setVisible(isOpenPGP && isOwnKey);
-
// update availability of buttons
- const auto userCanSignUserIDs = userHasCertificationKey();
- ui.addUserIDBtn->setEnabled(canBeUsedForSecretKeyOperations(key));
- ui.setPrimaryUserIDBtn->setEnabled(false); // requires a selected user ID
- ui.certifyBtn->setEnabled(isLocalKey && keyCanBeCertified && userCanSignUserIDs);
- ui.webOfTrustBtn->setEnabled(isLocalKey);
- ui.revokeCertificationsBtn->setEnabled(userCanSignUserIDs && isLocalKey);
- ui.revokeUserIDBtn->setEnabled(false); // requires a selected user ID
ui.changeExpirationAction->setEnabled(canBeUsedForSecretKeyOperations(key));
- ui.changePassphraseBtn->setEnabled(isSecretKeyStoredInKeyRing(key));
- ui.genRevokeBtn->setEnabled(canBeUsedForSecretKeyOperations(key));
// update values of protocol-independent UI elements
ui.validFromField->setValue(Formatting::creationDateString(key), Formatting::accessibleCreationDate(key));
ui.expiresField->setValue(Formatting::expirationDateString(key, i18nc("Valid until:", "unlimited")), Formatting::accessibleExpirationDate(key));
ui.fingerprintField->setValue(Formatting::prettyID(key.primaryFingerprint()), Formatting::accessibleHexID(key.primaryFingerprint()));
- if (DeVSCompliance::isCompliant()) {
- ui.complianceField->setValue(Kleo::Formatting::complianceStringForKey(key));
- }
-}
-
-void CertificateDetailsWidget::Private::updateUserIDActions()
-{
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- const auto singleUserID = userIDs.size() == 1 ? userIDs.front() : GpgME::UserID{};
- const bool isPrimaryUserID = !singleUserID.isNull() && (ui.userIDTable->selectedItems().front() == ui.userIDTable->topLevelItem(0));
- ui.setPrimaryUserIDBtn->setEnabled(!singleUserID.isNull() //
- && !isPrimaryUserID //
- && !Kleo::isRevokedOrExpired(singleUserID) //
- && canBeUsedForSecretKeyOperations(key));
- ui.revokeUserIDBtn->setEnabled(!singleUserID.isNull() && canCreateCertifications(key) && canRevokeUserID(singleUserID));
-}
-
-void CertificateDetailsWidget::Private::setUpUserIDTable()
-{
- ui.userIDTable->clear();
-
- QStringList headers = {i18n("Email"), i18n("Name"), i18n("Trust Level"), i18n("Tags")};
- ui.userIDTable->setColumnCount(headers.count());
- ui.userIDTable->setColumnWidth(0, 200);
- ui.userIDTable->setColumnWidth(1, 200);
- ui.userIDTable->setHeaderLabels(headers);
-
- const auto uids = key.userIDs();
- for (unsigned int i = 0; i < uids.size(); ++i) {
- const auto &uid = uids[i];
- auto item = new QTreeWidgetItem;
- const QString toolTip = tofuTooltipString(uid);
- item->setData(0, Qt::UserRole, QVariant::fromValue(uid));
-
- auto pMail = Kleo::Formatting::prettyEMail(uid);
- auto pName = Kleo::Formatting::prettyName(uid);
-
- item->setData(0, Qt::DisplayRole, pMail);
- item->setData(0, Qt::ToolTipRole, toolTip);
- item->setData(0, Qt::AccessibleTextRole, pMail.isEmpty() ? i18nc("text for screen readers for an empty email address", "no email") : pMail);
- item->setData(1, Qt::DisplayRole, pName);
- item->setData(1, Qt::ToolTipRole, toolTip);
-
- item->setData(2, Qt::DecorationRole, trustLevelIcon(uid));
- item->setData(2, Qt::DisplayRole, trustLevelText(uid));
- item->setData(2, Qt::ToolTipRole, toolTip);
-
- GpgME::Error err;
- QStringList tagList;
- for (const auto &tag : uid.remarks(Tags::tagKeys(), err)) {
- if (err) {
- qCWarning(KLEOPATRA_LOG) << "Getting remarks for user ID" << uid.id() << "failed:" << err;
- }
- tagList << QString::fromStdString(tag);
+ ui.primaryUserIdField->setValue(Formatting::prettyUserID(key.userID(0)));
+
+ QString storage;
+ const auto &subkey = key.subkey(0);
+ if (!key.hasSecret()) {
+ storage = i18nc("not applicable", "n/a");
+ } else if (key.subkey(0).isCardKey()) {
+ if (const char *serialNo = subkey.cardSerialNumber()) {
+ storage = i18nc("smart card <serial number>", "smart card %1", QString::fromUtf8(serialNo));
+ } else {
+ storage = i18n("smart card");
}
- qCDebug(KLEOPATRA_LOG) << "tagList:" << tagList;
- const auto tags = tagList.join(QStringLiteral("; "));
- item->setData(3, Qt::DisplayRole, tags);
- item->setData(3, Qt::ToolTipRole, toolTip);
+ } else if (key.hasSecret() && !subkey.isSecret()) {
+ storage = i18nc("key is 'offline key', i.e. secret key is not stored on this computer", "offline");
+ } else if (subkey.isSecret()) {
+ storage = i18n("on this computer");
+ } else {
+ i18nc("unknown storage location", "unknown");
+ }
- ui.userIDTable->addTopLevelItem(item);
+ if (!key.subkey(0).isSecret()) {
+ i18n("None");
+ } else if (key.subkey(0).cardSerialNumber()) {
+ storage = i18n("Smartcard");
}
- ui.userIDTable->restoreColumnLayout(QStringLiteral("UserIDTable"));
- if (!Tags::tagsEnabled()) {
- ui.userIDTable->hideColumn(3);
+ ui.privateKeyInfoField->setValue(storage);
+ if (DeVSCompliance::isCompliant()) {
+ ui.complianceField->setValue(Kleo::Formatting::complianceStringForKey(key));
}
}
void CertificateDetailsWidget::Private::setUpSMIMEAdressList()
{
ui.smimeAddressList->clear();
const auto *const emailField = attributeField(QStringLiteral("EMAIL"));
// add email address from primary user ID if not listed already as attribute field
if (!emailField) {
const auto ownerId = key.userID(0);
const Kleo::DN dn(ownerId.id());
const QString dnEmail = dn[QStringLiteral("EMAIL")];
if (!dnEmail.isEmpty()) {
ui.smimeAddressList->addItem(dnEmail);
}
}
if (key.numUserIDs() > 1) {
// iterate over the secondary user IDs
#ifdef USE_RANGES
for (const auto uids = key.userIDs(); const auto &uid : std::ranges::subrange(std::next(uids.begin()), uids.end())) {
#else
const auto uids = key.userIDs();
for (auto it = std::next(uids.begin()); it != uids.end(); ++it) {
const auto &uid = *it;
#endif
const auto name = Kleo::Formatting::prettyName(uid);
const auto email = Kleo::Formatting::prettyEMail(uid);
QString itemText;
if (name.isEmpty() && !email.isEmpty()) {
// skip email addresses already listed in email attribute field
if (emailField && email == emailField->value()) {
continue;
}
itemText = email;
} else {
// S/MIME certificates sometimes contain urls where both
// name and mail is empty. In that case we print whatever
// the uid is as name.
//
// Can be ugly like (3:uri24:http://ca.intevation.org), but
// this is better then showing an empty entry.
itemText = QString::fromUtf8(uid.id());
}
// avoid duplicate entries in the list
if (ui.smimeAddressList->findItems(itemText, Qt::MatchExactly).empty()) {
ui.smimeAddressList->addItem(itemText);
}
}
}
if (ui.smimeAddressList->count() == 0) {
- ui.smimeRelatedAddresses->setVisible(false);
- ui.smimeAddressList->setVisible(false);
- }
-}
-
-void CertificateDetailsWidget::Private::revokeUserID(const GpgME::UserID &userId)
-{
- const QString message =
- xi18nc("@info", "<para>Do you really want to revoke the user ID<nl/><emphasis>%1</emphasis> ?</para>", QString::fromUtf8(userId.id()));
- auto confirmButton = KStandardGuiItem::ok();
- confirmButton.setText(i18nc("@action:button", "Revoke User ID"));
- confirmButton.setToolTip({});
- const auto choice = KMessageBox::questionTwoActions(q->window(),
- message,
- i18nc("@title:window", "Confirm Revocation"),
- confirmButton,
- KStandardGuiItem::cancel(),
- {},
- KMessageBox::Notify | KMessageBox::WindowModal);
- if (choice != KMessageBox::ButtonCode::PrimaryAction) {
- return;
- }
-
- auto cmd = new Commands::RevokeUserIDCommand(userId);
- cmd->setParentWidget(q);
- connect(cmd, &Command::finished, q, [this]() {
- ui.userIDTable->setEnabled(true);
- // the Revoke User ID button will be updated by the key update
- updateKey();
- });
- ui.userIDTable->setEnabled(false);
- ui.revokeUserIDBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::revokeSelectedUserID()
-{
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- if (userIDs.size() != 1) {
- return;
+ ui.tabWidget->setTabVisible(1, false);
}
- revokeUserID(userIDs.front());
}
void CertificateDetailsWidget::Private::changeExpiration()
{
auto cmd = new Kleo::Commands::ChangeExpiryCommand(key);
QObject::connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished, q, [this]() {
ui.changeExpirationAction->setEnabled(true);
});
ui.changeExpirationAction->setEnabled(false);
cmd->start();
}
-void CertificateDetailsWidget::Private::changePassphrase()
-{
- auto cmd = new Kleo::Commands::ChangePassphraseCommand(key);
- QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished, q, [this]() {
- ui.changePassphraseBtn->setEnabled(true);
- });
- ui.changePassphraseBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::genRevokeCert()
-{
- auto cmd = new Kleo::Commands::GenRevokeCommand(key);
- QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished, q, [this]() {
- ui.genRevokeBtn->setEnabled(true);
- });
- ui.genRevokeBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::refreshCertificate()
-{
- auto cmd = new Kleo::RefreshCertificateCommand{key};
- QObject::connect(cmd, &Kleo::RefreshCertificateCommand::finished, q, [this]() {
- ui.refreshBtn->setEnabled(true);
- });
- ui.refreshBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::certifyUserIDs()
-{
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- auto cmd = userIDs.empty() ? new Kleo::Commands::CertifyCertificateCommand{key} //
- : new Kleo::Commands::CertifyCertificateCommand{userIDs};
- QObject::connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() {
- updateKey();
- ui.certifyBtn->setEnabled(true);
- });
- ui.certifyBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::revokeCertifications()
-{
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- auto cmd = userIDs.empty() ? new Kleo::Commands::RevokeCertificationCommand{key} //
- : new Kleo::Commands::RevokeCertificationCommand{userIDs};
- QObject::connect(cmd, &Kleo::Command::finished, q, [this]() {
- updateKey();
- ui.revokeCertificationsBtn->setEnabled(true);
- });
- ui.revokeCertificationsBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::webOfTrustClicked()
-{
- QScopedPointer<WebOfTrustDialog> dlg(new WebOfTrustDialog(q));
- dlg->setKey(key);
- dlg->exec();
-}
-
-void CertificateDetailsWidget::Private::exportClicked()
-{
- QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
- dlg->setKey(key);
- dlg->exec();
-}
-
-void CertificateDetailsWidget::Private::addUserID()
-{
- auto cmd = new Kleo::Commands::AddUserIDCommand(key);
- QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished, q, [this]() {
- ui.addUserIDBtn->setEnabled(true);
- updateKey();
- });
- ui.addUserIDBtn->setEnabled(false);
- cmd->start();
-}
-
-void CertificateDetailsWidget::Private::setPrimaryUserID(const GpgME::UserID &uid)
-{
- auto userId = uid;
- if (userId.isNull()) {
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- if (userIDs.size() != 1) {
- return;
- }
- userId = userIDs.front();
- }
-
- auto cmd = new Kleo::Commands::SetPrimaryUserIDCommand(userId);
- QObject::connect(cmd, &Kleo::Commands::SetPrimaryUserIDCommand::finished, q, [this]() {
- ui.userIDTable->setEnabled(true);
- // the Flag As Primary button will be updated by the key update
- updateKey();
- });
- ui.userIDTable->setEnabled(false);
- ui.setPrimaryUserIDBtn->setEnabled(false);
- cmd->start();
-}
-
namespace
{
void ensureThatKeyDetailsAreLoaded(GpgME::Key &key)
{
if (key.userID(0).numSignatures() == 0) {
key.update();
}
}
}
void CertificateDetailsWidget::Private::keysMayHaveChanged()
{
auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
if (!newKey.isNull()) {
ensureThatKeyDetailsAreLoaded(newKey);
setUpdatedKey(newKey);
}
}
-void CertificateDetailsWidget::Private::showTrustChainDialog()
-{
- QScopedPointer<TrustChainDialog> dlg(new TrustChainDialog(q));
- dlg->setKey(key);
- dlg->exec();
-}
-
-void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QPoint &p)
-{
- const auto userIDs = selectedUserIDs(ui.userIDTable);
- const auto singleUserID = (userIDs.size() == 1) ? userIDs.front() : GpgME::UserID{};
- const bool isPrimaryUserID = !singleUserID.isNull() && (ui.userIDTable->selectedItems().front() == ui.userIDTable->topLevelItem(0));
- const bool canSignUserIDs = userHasCertificationKey();
- const auto isLocalKey = !isRemoteKey(key);
- const auto keyCanBeCertified = Kleo::canBeCertified(key);
-
- auto menu = new QMenu(q);
- if (key.hasSecret()) {
- auto action =
- menu->addAction(QIcon::fromTheme(QStringLiteral("favorite")), i18nc("@action:inmenu", "Flag as Primary User ID"), q, [this, singleUserID]() {
- setPrimaryUserID(singleUserID);
- });
- action->setEnabled(!singleUserID.isNull() //
- && !isPrimaryUserID //
- && !Kleo::isRevokedOrExpired(singleUserID) //
- && canBeUsedForSecretKeyOperations(key));
- }
- {
- const auto actionText = userIDs.empty() ? i18nc("@action:inmenu", "Certify User IDs...")
- : i18ncp("@action:inmenu", "Certify User ID...", "Certify User IDs...", userIDs.size());
- auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")), actionText, q, [this]() {
- certifyUserIDs();
- });
- action->setEnabled(isLocalKey && keyCanBeCertified && canSignUserIDs);
- }
- if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
- const auto actionText = userIDs.empty() ? i18nc("@action:inmenu", "Revoke Certifications...")
- : i18ncp("@action:inmenu", "Revoke Certification...", "Revoke Certifications...", userIDs.size());
- auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), actionText, q, [this]() {
- revokeCertifications();
- });
- action->setEnabled(isLocalKey && canSignUserIDs);
- }
-#ifdef MAILAKONADI_ENABLED
- if (key.hasSecret()) {
- auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")),
- i18nc("@action:inmenu", "Publish at Mail Provider ..."),
- q,
- [this, singleUserID]() {
- auto cmd = new Kleo::Commands::ExportOpenPGPCertToProviderCommand(singleUserID);
- ui.userIDTable->setEnabled(false);
- connect(cmd, &Kleo::Commands::ExportOpenPGPCertToProviderCommand::finished, q, [this]() {
- ui.userIDTable->setEnabled(true);
- });
- cmd->start();
- });
- action->setEnabled(!singleUserID.isNull());
- }
-#endif // MAILAKONADI_ENABLED
- {
- auto action =
- menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), i18nc("@action:inmenu", "Revoke User ID"), q, [this, singleUserID]() {
- revokeUserID(singleUserID);
- });
- action->setEnabled(!singleUserID.isNull() && canCreateCertifications(key) && canRevokeUserID(singleUserID));
- }
- connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
- menu->popup(ui.userIDTable->viewport()->mapToGlobal(p));
-}
-
-void CertificateDetailsWidget::Private::showMoreDetails()
-{
- if (key.protocol() == GpgME::CMS) {
- auto cmd = new Kleo::Commands::DumpCertificateCommand(key);
- cmd->setParentWidget(q);
- cmd->setUseDialog(true);
- cmd->start();
- } else {
- auto dlg = new SubKeysDialog{q};
- dlg->setAttribute(Qt::WA_DeleteOnClose);
- dlg->setKey(key);
- dlg->open();
- }
-}
-
-QString CertificateDetailsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const
-{
- const auto tofu = uid.tofuInfo();
- if (tofu.isNull()) {
- return QString();
- }
-
- QString html = QStringLiteral("<table border=\"0\" cell-padding=\"5\">");
- const auto appendRow = [&html](const QString &lbl, const QString &val) {
- html += QStringLiteral(
- "<tr>"
- "<th style=\"text-align: right; padding-right: 5px; white-space: nowrap;\">%1:</th>"
- "<td style=\"white-space: nowrap;\">%2</td>"
- "</tr>")
- .arg(lbl, val);
- };
- const auto appendHeader = [this, &html](const QString &hdr) {
- html += QStringLiteral("<tr><th colspan=\"2\" style=\"background-color: %1; color: %2\">%3</th></tr>")
- .arg(q->palette().highlight().color().name(), q->palette().highlightedText().color().name(), hdr);
- };
- const auto dateTime = [](long ts) {
- QLocale l;
- return ts == 0 ? i18n("never") : l.toString(QDateTime::fromSecsSinceEpoch(ts), QLocale::ShortFormat);
- };
- appendHeader(i18n("Signing"));
- appendRow(i18n("First message"), dateTime(tofu.signFirst()));
- appendRow(i18n("Last message"), dateTime(tofu.signLast()));
- appendRow(i18n("Message count"), QString::number(tofu.signCount()));
- appendHeader(i18n("Encryption"));
- appendRow(i18n("First message"), dateTime(tofu.encrFirst()));
- appendRow(i18n("Last message"), dateTime(tofu.encrLast()));
- appendRow(i18n("Message count"), QString::number(tofu.encrCount()));
-
- html += QStringLiteral("</table>");
- // Make sure the tooltip string is different for each UserID, even if the
- // data are the same, otherwise the tooltip is not updated and moved when
- // user moves mouse from one row to another.
- html += QStringLiteral("<!-- %1 //-->").arg(QString::fromUtf8(uid.id()));
- return html;
-}
-
QIcon CertificateDetailsWidget::Private::trustLevelIcon(const GpgME::UserID &uid) const
{
if (updateInProgress) {
return QIcon::fromTheme(QStringLiteral("emblem-question"));
}
switch (uid.validity()) {
case GpgME::UserID::Unknown:
case GpgME::UserID::Undefined:
return QIcon::fromTheme(QStringLiteral("emblem-question"));
case GpgME::UserID::Never:
return QIcon::fromTheme(QStringLiteral("emblem-error"));
case GpgME::UserID::Marginal:
return QIcon::fromTheme(QStringLiteral("emblem-warning"));
case GpgME::UserID::Full:
case GpgME::UserID::Ultimate:
return QIcon::fromTheme(QStringLiteral("emblem-success"));
}
return {};
}
QString CertificateDetailsWidget::Private::trustLevelText(const GpgME::UserID &uid) const
{
return updateInProgress ? i18n("Updating...") : Formatting::validityShort(uid);
}
namespace
{
auto isGood(const GpgME::UserID::Signature &signature)
{
return signature.status() == GpgME::UserID::Signature::NoError //
&& !signature.isInvalid() //
&& 0x10 <= signature.certClass() && signature.certClass() <= 0x13;
}
auto accumulateTrustDomains(const std::vector<GpgME::UserID::Signature> &signatures)
{
return std::accumulate(std::begin(signatures), std::end(signatures), std::set<QString>(), [](auto domains, const auto &signature) {
if (isGood(signature) && signature.isTrustSignature()) {
domains.insert(Formatting::trustSignatureDomain(signature));
}
return domains;
});
}
auto accumulateTrustDomains(const std::vector<GpgME::UserID> &userIds)
{
return std::accumulate(std::begin(userIds), std::end(userIds), std::set<QString>(), [](auto domains, const auto &userID) {
const auto newDomains = accumulateTrustDomains(userID.signatures());
std::copy(std::begin(newDomains), std::end(newDomains), std::inserter(domains, std::end(domains)));
return domains;
});
}
}
void CertificateDetailsWidget::Private::setupPGPProperties()
{
- setUpUserIDTable();
+ ui.tabWidget->setTabVisible(0, true);
+ ui.tabWidget->setTabVisible(1, false);
+ ui.tabWidget->setTabVisible(2, true);
+ ui.tabWidget->setTabVisible(3, true);
+ ui.tabWidget->setTabVisible(4, false);
+
+ ui.userIDs->setKey(key);
+ ui.subKeysWidget->setKey(key);
+ ui.webOfTrustWidget->setKey(key);
const auto trustDomains = accumulateTrustDomains(key.userIDs());
ui.trustedIntroducerField->setVisible(!trustDomains.empty());
ui.trustedIntroducerField->setValue(QStringList(std::begin(trustDomains), std::end(trustDomains)).join(u", "));
-
- ui.refreshBtn->setToolTip(i18nc("@info:tooltip", "Update the key from external sources."));
}
static QString formatDNToolTip(const Kleo::DN &dn)
{
QString html = QStringLiteral("<table border=\"0\" cell-spacing=15>");
const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) {
const QString val = dn[attr];
if (!val.isEmpty()) {
html += QStringLiteral(
"<tr><th style=\"text-align: left; white-space: nowrap\">%1:</th>"
"<td style=\"white-space: nowrap\">%2</td>"
"</tr>")
.arg(lbl, val);
}
};
appendRow(i18n("Common Name"), QStringLiteral("CN"));
appendRow(i18n("Organization"), QStringLiteral("O"));
appendRow(i18n("Street"), QStringLiteral("STREET"));
appendRow(i18n("City"), QStringLiteral("L"));
appendRow(i18n("State"), QStringLiteral("ST"));
appendRow(i18n("Country"), QStringLiteral("C"));
html += QStringLiteral("</table>");
return html;
}
void CertificateDetailsWidget::Private::setupSMIMEProperties()
{
+ ui.tabWidget->setTabVisible(0, false); // User IDs
+ ui.tabWidget->setTabVisible(1, true); // smime addresses;
+ ui.tabWidget->setTabVisible(2, false); // subkeys
+ ui.tabWidget->setTabVisible(3, false); // Certifications
+ ui.tabWidget->setTabVisible(4, true); // trust chain
+
+ ui.trustChainWidget->setKey(key);
const auto ownerId = key.userID(0);
const Kleo::DN dn(ownerId.id());
for (const auto &[attributeName, field] : ui.smimeAttributeFields) {
const QString attributeValue = dn[attributeName];
field->setValue(attributeValue);
field->setVisible(!attributeValue.isEmpty());
}
ui.smimeTrustLevelField->setIcon(trustLevelIcon(ownerId));
ui.smimeTrustLevelField->setValue(trustLevelText(ownerId));
const Kleo::DN issuerDN(key.issuerName());
const QString issuerCN = issuerDN[QStringLiteral("CN")];
const QString issuer = issuerCN.isEmpty() ? QString::fromUtf8(key.issuerName()) : issuerCN;
ui.smimeIssuerField->setValue(issuer);
ui.smimeIssuerField->setToolTip(formatDNToolTip(issuerDN));
ui.showIssuerCertificateAction->setEnabled(!key.isRoot());
setUpSMIMEAdressList();
-
- ui.refreshBtn->setToolTip(i18nc("@info:tooltip", "Update the CRLs and do a full validation check of the certificate."));
}
void CertificateDetailsWidget::Private::showIssuerCertificate()
{
// there is either one or no parent key
const auto parentKeys = KeyCache::instance()->findIssuers(key, KeyCache::NoOption);
if (parentKeys.empty()) {
KMessageBox::error(q, i18n("The issuer certificate could not be found locally."));
return;
}
auto cmd = new Kleo::Commands::DetailsCommand(parentKeys.front());
cmd->setParentWidget(q);
cmd->start();
}
void CertificateDetailsWidget::Private::copyFingerprintToClipboard()
{
if (auto clipboard = QGuiApplication::clipboard()) {
clipboard->setText(QString::fromLatin1(key.primaryFingerprint()));
}
}
CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
CertificateDetailsWidget::~CertificateDetailsWidget() = default;
void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &, const std::vector<GpgME::Key> &keys, const QString &, const GpgME::Error &)
{
- updateInProgress = false;
+ setUpdateInProgress(false);
if (keys.size() != 1) {
qCWarning(KLEOPATRA_LOG) << "Invalid keylist result in update.";
return;
}
// As we listen for keysmayhavechanged we get the update
// after updating the keycache.
KeyCache::mutableInstance()->insert(keys);
}
void CertificateDetailsWidget::Private::updateKey()
{
key.update();
setUpdatedKey(key);
}
void CertificateDetailsWidget::Private::setUpdatedKey(const GpgME::Key &k)
{
key = k;
setupCommonProperties();
if (key.protocol() == GpgME::OpenPGP) {
setupPGPProperties();
} else {
setupSMIMEProperties();
}
}
void CertificateDetailsWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() == GpgME::CMS) {
// For everything but S/MIME this should be quick
// and we don't need to show another status.
- d->updateInProgress = true;
+ d->setUpdateInProgress(true);
}
d->setUpdatedKey(key);
// Run a keylistjob with full details (TOFU / Validate)
QGpgME::KeyListJob *job =
key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp()->keyListJob(false, true, true) : QGpgME::smime()->keyListJob(false, true, true);
auto ctx = QGpgME::Job::context(job);
ctx->addKeyListMode(GpgME::WithTofu);
ctx->addKeyListMode(GpgME::SignatureNotations);
if (key.hasSecret()) {
ctx->addKeyListMode(GpgME::WithSecret);
}
// Windows QGpgME new style connect problem makes this necessary.
connect(job,
SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>, QString, GpgME::Error)),
this,
SLOT(keyListDone(GpgME::KeyListResult, std::vector<GpgME::Key>, QString, GpgME::Error)));
job->start(QStringList() << QLatin1StringView(key.primaryFingerprint()));
}
GpgME::Key CertificateDetailsWidget::key() const
{
return d->key;
}
+void CertificateDetailsWidget::Private::setUpdateInProgress(bool updateInProgress)
+{
+ this->updateInProgress = updateInProgress;
+ ui.userIDs->setUpdateInProgress(updateInProgress);
+}
+
#include "moc_certificatedetailswidget.cpp"
diff --git a/src/dialogs/subkeyswidget.cpp b/src/dialogs/subkeyswidget.cpp
index 008c0e5dd..6715884b3 100644
--- a/src/dialogs/subkeyswidget.cpp
+++ b/src/dialogs/subkeyswidget.cpp
@@ -1,426 +1,389 @@
/*
dialogs/subkeyswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 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 "subkeyswidget.h"
#include "commands/addsubkeycommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/exportsecretsubkeycommand.h"
#include "commands/importpaperkeycommand.h"
#include "commands/keytocardcommand.h"
#include "exportdialog.h"
#include <kleopatra_debug.h>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/TreeWidget>
#include <KConfigGroup>
#include <KLocalizedString>
+#include <KSeparator>
#include <KSharedConfig>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
Q_DECLARE_METATYPE(GpgME::Subkey)
using namespace Kleo;
using namespace Kleo::Commands;
class SubKeysWidget::Private
{
SubKeysWidget *const q;
public:
Private(SubKeysWidget *qq)
: q{qq}
, ui{qq}
{
ui.subkeysTree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui.subkeysTree, &QAbstractItemView::customContextMenuRequested, q, [this](const QPoint &p) {
tableContextMenuRequested(p);
});
connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, q, [this]() {
keysMayHaveChanged();
});
}
void changeValidity(const GpgME::Subkey &subkey);
void exportSSH(const GpgME::Subkey &subkey);
void keyToCard(const GpgME::Subkey &subkey);
void exportSecret(const GpgME::Subkey &subkey);
void importPaperKey();
+ void addSubkey();
private:
void tableContextMenuRequested(const QPoint &p);
void keysMayHaveChanged();
public:
GpgME::Key key;
public:
struct UI {
QVBoxLayout *mainLayout;
TreeWidget *subkeysTree;
- QPushButton *addSubkeyButton = nullptr;
QPushButton *changeValidityBtn = nullptr;
QPushButton *exportOpenSSHBtn = nullptr;
QPushButton *restoreBtn = nullptr;
QPushButton *transferToSmartcardBtn = nullptr;
QPushButton *exportSecretBtn = nullptr;
+ QPushButton *addSubkeyButton = nullptr;
UI(QWidget *widget)
{
mainLayout = new QVBoxLayout{widget};
- mainLayout->setContentsMargins(0, 0, 0, 0);
-
- auto subkeysTreeLabel = new QLabel{i18nc("@label", "Subkeys:"), widget};
- mainLayout->addWidget(subkeysTreeLabel);
+ mainLayout->setContentsMargins({});
+ mainLayout->setSpacing(0);
subkeysTree = new TreeWidget{widget};
- subkeysTreeLabel->setBuddy(subkeysTree);
subkeysTree->setAccessibleName(i18nc("@label", "Subkeys"));
subkeysTree->setRootIsDecorated(false);
subkeysTree->setHeaderLabels({
i18nc("@title:column", "ID"),
i18nc("@title:column", "Fingerprint"),
i18nc("@title:column", "Valid From"),
i18nc("@title:column", "Valid Until"),
i18nc("@title:column", "Status"),
i18nc("@title:column", "Algorithm"),
i18nc("@title:column", "Usage"),
i18nc("@title:column", "Storage"),
});
mainLayout->addWidget(subkeysTree);
+ auto separator = new KSeparator(widget);
+ mainLayout->addWidget(separator);
+
{
auto buttonRow = new QHBoxLayout;
+ addSubkeyButton = new QPushButton(i18nc("@action:button", "Add subkey"));
+ buttonRow->addWidget(addSubkeyButton);
+
changeValidityBtn = new QPushButton(i18nc("@action:button", "Change validity"), widget);
buttonRow->addWidget(changeValidityBtn);
exportOpenSSHBtn = new QPushButton{i18nc("@action:button", "Export OpenSSH key"), widget};
buttonRow->addWidget(exportOpenSSHBtn);
restoreBtn = new QPushButton(i18nc("@action:button", "Restore printed backup"), widget);
buttonRow->addWidget(restoreBtn);
transferToSmartcardBtn = new QPushButton(i18nc("@action:button", "Transfer to smartcard"), widget);
buttonRow->addWidget(transferToSmartcardBtn);
exportSecretBtn = new QPushButton(i18nc("@action:button", "Export secret subkey"), widget);
buttonRow->addWidget(exportSecretBtn);
buttonRow->addStretch(1);
mainLayout->addLayout(buttonRow);
}
}
} ui;
};
void SubKeysWidget::Private::changeValidity(const GpgME::Subkey &subkey)
{
+ ui.changeValidityBtn->setEnabled(false);
auto cmd = new ChangeExpiryCommand(subkey.parent());
cmd->setSubkey(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &ChangeExpiryCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
key.update();
q->setKey(key);
+ ui.changeValidityBtn->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
}
void SubKeysWidget::Private::exportSSH(const GpgME::Subkey &subkey)
{
QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
dlg->setKey(subkey, static_cast<unsigned int>(GpgME::Context::ExportSSH));
dlg->exec();
}
void SubKeysWidget::Private::importPaperKey()
{
+ ui.restoreBtn->setEnabled(false);
auto cmd = new ImportPaperKeyCommand(key);
ui.subkeysTree->setEnabled(false);
connect(cmd, &ImportPaperKeyCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
+ ui.restoreBtn->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
}
void SubKeysWidget::Private::keyToCard(const GpgME::Subkey &subkey)
{
auto cmd = new KeyToCardCommand(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &KeyToCardCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
}
void SubKeysWidget::Private::exportSecret(const GpgME::Subkey &subkey)
{
+ ui.exportSecretBtn->setEnabled(false);
auto cmd = new ExportSecretSubkeyCommand{{subkey}};
ui.subkeysTree->setEnabled(false);
connect(cmd, &ExportSecretSubkeyCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
+ ui.exportSecretBtn->setEnabled(true);
+ });
+ cmd->setParentWidget(q);
+ cmd->start();
+}
+
+void SubKeysWidget::Private::addSubkey()
+{
+ ui.addSubkeyButton->setEnabled(false);
+ const auto cmd = new AddSubkeyCommand(q->key());
+ connect(cmd, &AddSubkeyCommand::finished, q, [this]() {
+ q->key().update();
+ ui.addSubkeyButton->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
}
void SubKeysWidget::Private::tableContextMenuRequested(const QPoint &p)
{
auto item = ui.subkeysTree->itemAt(p);
if (!item) {
return;
}
const auto subkey = item->data(0, Qt::UserRole).value<GpgME::Subkey>();
const bool isOwnKey = subkey.parent().hasSecret();
const bool secretSubkeyStoredInKeyRing = subkey.isSecret() && !subkey.isCardKey();
auto menu = new QMenu(q);
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
if (isOwnKey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("change-date-symbolic")), i18n("Change validity"), q, [this, subkey]() {
changeValidity(subkey);
});
action->setEnabled(canBeUsedForSecretKeyOperations(subkey.parent()));
}
if (subkey.canAuthenticate()) {
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")), i18n("Export OpenSSH key"), q, [this, subkey]() {
exportSSH(subkey);
});
}
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-import")), i18n("Restore printed backup"), q, [this, subkey]() {
importPaperKey();
});
action->setEnabled(!secretSubkeyStoredInKeyRing);
if (isOwnKey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("send-to-symbolic")), i18n("Transfer to smartcard"), q, [this, subkey]() {
keyToCard(subkey);
});
action->setEnabled(secretSubkeyStoredInKeyRing && !KeyToCardCommand::getSuitableCards(subkey).empty());
}
const bool isPrimarySubkey = subkey.keyID() == key.keyID();
if (isOwnKey && !isPrimarySubkey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")), i18n("Export secret subkey"), q, [this, subkey]() {
exportSecret(subkey);
});
action->setEnabled(secretSubkeyStoredInKeyRing);
}
menu->popup(ui.subkeysTree->viewport()->mapToGlobal(p));
}
void SubKeysWidget::Private::keysMayHaveChanged()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
const auto updatedKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
if (!updatedKey.isNull()) {
q->setKey(updatedKey);
}
}
SubKeysWidget::SubKeysWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
connect(d->ui.subkeysTree, &TreeWidget::currentItemChanged, this, [this] {
const auto currentIndex = d->ui.subkeysTree->currentIndex().row();
const auto &subkey = d->key.subkey(currentIndex);
const bool secretSubkeyStoredInKeyRing = subkey.isSecret() && !subkey.isCardKey();
d->ui.exportOpenSSHBtn->setEnabled(subkey.canAuthenticate());
d->ui.changeValidityBtn->setEnabled(d->key.hasSecret() && canBeUsedForSecretKeyOperations(subkey.parent()));
d->ui.exportSecretBtn->setEnabled(d->key.hasSecret() && subkey.fingerprint() != d->key.primaryFingerprint() && secretSubkeyStoredInKeyRing);
d->ui.restoreBtn->setEnabled(!secretSubkeyStoredInKeyRing);
d->ui.transferToSmartcardBtn->setEnabled(secretSubkeyStoredInKeyRing && !KeyToCardCommand::getSuitableCards(subkey).empty());
});
connect(d->ui.changeValidityBtn, &QPushButton::clicked, this, [this] {
d->changeValidity(d->key.subkey(d->ui.subkeysTree->currentIndex().row()));
});
connect(d->ui.exportOpenSSHBtn, &QPushButton::clicked, this, [this] {
d->exportSSH(d->key.subkey(d->ui.subkeysTree->currentIndex().row()));
});
connect(d->ui.restoreBtn, &QPushButton::clicked, this, [this] {
d->importPaperKey();
});
connect(d->ui.transferToSmartcardBtn, &QPushButton::clicked, this, [this] {
d->keyToCard(d->key.subkey(d->ui.subkeysTree->currentIndex().row()));
});
connect(d->ui.exportSecretBtn, &QPushButton::clicked, this, [this] {
d->exportSecret(d->key.subkey(d->ui.subkeysTree->currentIndex().row()));
});
+ connect(d->ui.addSubkeyButton, &QPushButton::clicked, this, [this]() {
+ d->addSubkey();
+ });
}
SubKeysWidget::~SubKeysWidget() = default;
void SubKeysWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::OpenPGP) {
return;
}
d->key = key;
const auto currentItem = d->ui.subkeysTree->currentItem();
const QByteArray selectedKeyFingerprint = currentItem ? QByteArray(currentItem->data(0, Qt::UserRole).value<GpgME::Subkey>().fingerprint()) : QByteArray();
d->ui.subkeysTree->clear();
const auto subkeys = key.subkeys();
for (const auto &subkey : subkeys) {
auto item = new QTreeWidgetItem;
item->setData(0, Qt::DisplayRole, Formatting::prettyID(subkey.keyID()));
item->setData(0, Qt::AccessibleTextRole, Formatting::accessibleHexID(subkey.keyID()));
item->setData(0, Qt::UserRole, QVariant::fromValue(subkey));
item->setData(1, Qt::DisplayRole, Formatting::prettyID(subkey.fingerprint()));
item->setData(1, Qt::AccessibleTextRole, Formatting::accessibleHexID(subkey.fingerprint()));
item->setData(2, Qt::DisplayRole, Kleo::Formatting::creationDateString(subkey));
item->setData(2, Qt::AccessibleTextRole, Formatting::accessibleCreationDate(subkey));
item->setData(3,
Qt::DisplayRole,
subkey.neverExpires() ? Kleo::Formatting::expirationDateString(subkey.parent()) : Kleo::Formatting::expirationDateString(subkey));
item->setData(3,
Qt::AccessibleTextRole,
subkey.neverExpires() ? Kleo::Formatting::accessibleExpirationDate(subkey.parent()) : Kleo::Formatting::accessibleExpirationDate(subkey));
item->setData(4, Qt::DisplayRole, Kleo::Formatting::validityShort(subkey));
item->setData(5, Qt::DisplayRole, Kleo::Formatting::prettyAlgorithmName(subkey.algoName()));
item->setData(6, Qt::DisplayRole, Kleo::Formatting::usageString(subkey));
const auto isPrimary = subkey.keyID() == key.keyID();
if (!key.hasSecret()) {
item->setData(7, Qt::DisplayRole, i18nc("not applicable", "n/a"));
} else if (subkey.isCardKey()) {
if (const char *serialNo = subkey.cardSerialNumber()) {
item->setData(7, Qt::DisplayRole, i18nc("smart card <serial number>", "smart card %1", QString::fromUtf8(serialNo)));
} else {
item->setData(7, Qt::DisplayRole, i18n("smart card"));
}
} else if (isPrimary && key.hasSecret() && !subkey.isSecret()) {
item->setData(7, Qt::DisplayRole, i18nc("key is 'offline key', i.e. secret key is not stored on this computer", "offline"));
} else if (subkey.isSecret()) {
item->setData(7, Qt::DisplayRole, i18n("on this computer"));
} else {
item->setData(7, Qt::DisplayRole, i18nc("unknown storage location", "unknown"));
}
d->ui.subkeysTree->addTopLevelItem(item);
if (subkey.fingerprint() == selectedKeyFingerprint) {
d->ui.subkeysTree->setCurrentItem(item);
}
}
d->ui.subkeysTree->header()->resizeSections(QHeaderView::ResizeToContents);
d->ui.changeValidityBtn->setVisible(key.hasSecret());
d->ui.exportSecretBtn->setVisible(key.hasSecret());
d->ui.transferToSmartcardBtn->setVisible(key.hasSecret());
+ d->ui.addSubkeyButton->setVisible(key.hasSecret());
+ d->ui.exportOpenSSHBtn->setEnabled(false);
if (!d->ui.subkeysTree->restoreColumnLayout(QStringLiteral("SubkeysWidget"))) {
d->ui.subkeysTree->hideColumn(1);
}
+ for (int i = 0; i < d->ui.subkeysTree->columnCount(); i++) {
+ d->ui.subkeysTree->resizeColumnToContents(i);
+ }
}
GpgME::Key SubKeysWidget::key() const
{
return d->key;
}
-SubKeysDialog::SubKeysDialog(QWidget *parent)
- : QDialog(parent)
-{
- setWindowTitle(i18nc("@title:window", "Subkeys Details"));
- auto l = new QVBoxLayout(this);
- const auto subKeysWidget = new SubKeysWidget(this);
- l->addWidget(subKeysWidget);
-
- auto bbox = new QDialogButtonBox(this);
-
- auto addSubkeyButton = new QPushButton(this);
- addSubkeyButton->setText(i18nc("@action:button", "Add Subkey"));
- addSubkeyButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
- addSubkeyButton->setObjectName(QLatin1String("AddSubkeyButton"));
- bbox->addButton(addSubkeyButton, QDialogButtonBox::ActionRole);
- connect(addSubkeyButton, &QPushButton::clicked, this, [this]() {
- const auto cmd = new AddSubkeyCommand(key());
- connect(cmd, &AddSubkeyCommand::finished, this, [this]() {
- key().update();
- });
- cmd->setParentWidget(this);
- cmd->start();
- });
-
- auto btn = bbox->addButton(QDialogButtonBox::Close);
- connect(btn, &QPushButton::clicked, this, &QDialog::accept);
- l->addWidget(bbox);
- readConfig();
-}
-
-SubKeysDialog::~SubKeysDialog()
-{
- writeConfig();
-}
-
-void SubKeysDialog::readConfig()
-{
- KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SubKeysDialog"));
- const QSize size = dialog.readEntry("Size", QSize(820, 280));
- if (size.isValid()) {
- resize(size);
- }
-}
-
-void SubKeysDialog::writeConfig()
-{
- KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SubKeysDialog"));
- dialog.writeEntry("Size", size());
- dialog.sync();
-}
-
-void SubKeysDialog::setKey(const GpgME::Key &key)
-{
- auto w = findChild<SubKeysWidget *>();
- Q_ASSERT(w);
- w->setKey(key);
- if (!key.hasSecret()) {
- findChild<QPushButton *>(QLatin1String("AddSubkeyButton"))->setVisible(false);
- }
-}
-
-GpgME::Key SubKeysDialog::key() const
-{
- auto w = findChild<SubKeysWidget *>();
- Q_ASSERT(w);
- return w->key();
-}
-
#include "moc_subkeyswidget.cpp"
diff --git a/src/dialogs/subkeyswidget.h b/src/dialogs/subkeyswidget.h
index 550f67721..df6cc0dd0 100644
--- a/src/dialogs/subkeyswidget.h
+++ b/src/dialogs/subkeyswidget.h
@@ -1,45 +1,29 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QDialog>
#include <QWidget>
namespace GpgME
{
class Key;
}
class SubKeysWidget : public QWidget
{
Q_OBJECT
public:
explicit SubKeysWidget(QWidget *parent = nullptr);
~SubKeysWidget() override;
void setKey(const GpgME::Key &key);
GpgME::Key key() const;
private:
class Private;
- friend class SubKeysDialog;
const QScopedPointer<Private> d;
};
-
-class SubKeysDialog : public QDialog
-{
- Q_OBJECT
-public:
- explicit SubKeysDialog(QWidget *parent = nullptr);
- ~SubKeysDialog() override;
-
- void setKey(const GpgME::Key &key);
- GpgME::Key key() const;
-
-private:
- void readConfig();
- void writeConfig();
-};
diff --git a/src/dialogs/trustchainwidget.cpp b/src/dialogs/trustchainwidget.cpp
index 8a06fe8f1..fad6e9c74 100644
--- a/src/dialogs/trustchainwidget.cpp
+++ b/src/dialogs/trustchainwidget.cpp
@@ -1,113 +1,80 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "trustchainwidget.h"
#include "ui_trustchainwidget.h"
#include "kleopatra_debug.h"
#include <QDialogButtonBox>
#include <QPushButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <gpgme++/key.h>
#include <Libkleo/Dn>
#include <Libkleo/KeyCache>
class TrustChainWidget::Private
{
public:
Private(TrustChainWidget *qq)
: q(qq)
{
Q_UNUSED(q);
}
GpgME::Key key;
Ui::TrustChainWidget ui;
private:
TrustChainWidget *const q;
};
TrustChainWidget::TrustChainWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
d->ui.setupUi(this);
}
TrustChainWidget::~TrustChainWidget()
{
}
void TrustChainWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::CMS) {
qCDebug(KLEOPATRA_LOG) << "Trust chain is only supported for CMS keys";
return;
}
d->key = key;
d->ui.treeWidget->clear();
const auto chain = Kleo::KeyCache::instance()->findIssuers(key, Kleo::KeyCache::RecursiveSearch | Kleo::KeyCache::IncludeSubject);
if (chain.empty()) {
return;
}
QTreeWidgetItem *last = nullptr;
if (!chain.back().isRoot()) {
last = new QTreeWidgetItem(d->ui.treeWidget);
last->setText(0, i18n("Issuer Certificate Not Found (%1)", Kleo::DN(chain.back().issuerName()).prettyDN()));
const QBrush &fg = d->ui.treeWidget->palette().brush(QPalette::Disabled, QPalette::WindowText);
last->setForeground(0, fg);
}
for (auto it = chain.rbegin(), end = chain.rend(); it != end; ++it) {
last = last ? new QTreeWidgetItem(last) : new QTreeWidgetItem(d->ui.treeWidget);
last->setText(0, Kleo::DN(it->userID(0).id()).prettyDN());
}
d->ui.treeWidget->expandAll();
}
GpgME::Key TrustChainWidget::key() const
{
return d->key;
}
-TrustChainDialog::TrustChainDialog(QWidget *parent)
- : QDialog(parent)
-{
- resize(650, 330);
- setWindowTitle(i18nc("@title:window", "Trust Chain"));
-
- auto l = new QVBoxLayout(this);
- l->addWidget(new TrustChainWidget(this));
-
- auto bbox = new QDialogButtonBox(this);
- auto btn = bbox->addButton(QDialogButtonBox::Close);
- connect(btn, &QPushButton::pressed, this, &QDialog::accept);
- l->addWidget(bbox);
-}
-
-TrustChainDialog::~TrustChainDialog()
-{
-}
-
-void TrustChainDialog::setKey(const GpgME::Key &key)
-{
- auto w = findChild<TrustChainWidget *>();
- Q_ASSERT(w);
- w->setKey(key);
-}
-
-GpgME::Key TrustChainDialog::key() const
-{
- auto w = findChild<TrustChainWidget *>();
- Q_ASSERT(w);
- return w->key();
-}
-
#include "moc_trustchainwidget.cpp"
diff --git a/src/dialogs/trustchainwidget.h b/src/dialogs/trustchainwidget.h
index b65a66848..1a2ea4f11 100644
--- a/src/dialogs/trustchainwidget.h
+++ b/src/dialogs/trustchainwidget.h
@@ -1,42 +1,30 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QDialog>
#include <QWidget>
namespace GpgME
{
class Key;
}
class TrustChainWidget : public QWidget
{
Q_OBJECT
public:
explicit TrustChainWidget(QWidget *parent = nullptr);
~TrustChainWidget() override;
void setKey(const GpgME::Key &key);
GpgME::Key key() const;
private:
class Private;
const QScopedPointer<Private> d;
};
-
-class TrustChainDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit TrustChainDialog(QWidget *parent = nullptr);
- ~TrustChainDialog() override;
-
- void setKey(const GpgME::Key &key);
- GpgME::Key key() const;
-};
diff --git a/src/dialogs/trustchainwidget.ui b/src/dialogs/trustchainwidget.ui
index 9ca007bfa..dd1d4fa90 100644
--- a/src/dialogs/trustchainwidget.ui
+++ b/src/dialogs/trustchainwidget.ui
@@ -1,42 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrustChainWidget</class>
<widget class="QWidget" name="TrustChainWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>330</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTreeWidget" name="treeWidget">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/src/dialogs/useridswidget.cpp b/src/dialogs/useridswidget.cpp
new file mode 100644
index 000000000..fbde1c3ea
--- /dev/null
+++ b/src/dialogs/useridswidget.cpp
@@ -0,0 +1,511 @@
+// SPDX-FileCopyrightText: 2024 g10 Code GmbH
+// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "useridswidget.h"
+
+#include "commands/adduseridcommand.h"
+#include "commands/certifycertificatecommand.h"
+#include "commands/revokecertificationcommand.h"
+#include "commands/revokeuseridcommand.h"
+#include "commands/setprimaryuseridcommand.h"
+#ifdef MAILAKONADI_ENABLED
+#include "commands/exportopenpgpcerttoprovidercommand.h"
+#endif // MAILAKONADI_ENABLED
+
+#include "utils/tags.h"
+
+#include <Libkleo/Formatting>
+#include <Libkleo/KeyCache>
+#include <Libkleo/KeyHelpers>
+#include <Libkleo/TreeView>
+#include <Libkleo/TreeWidget>
+#include <Libkleo/UserIDListModel>
+
+#include <KLocalizedString>
+#include <KMessageBox>
+#include <KSeparator>
+
+#include <QDateTime>
+#include <QDialogButtonBox>
+#include <QHeaderView>
+#include <QMenu>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include <QGpgME/KeyListJob>
+#include <QGpgME/Protocol>
+
+#include <gpgme++/key.h>
+#include <gpgme++/keylistresult.h>
+#include <gpgme++/tofuinfo.h>
+
+#include "kleopatra_debug.h"
+
+using namespace Kleo;
+
+namespace
+{
+std::vector<GpgME::UserID> selectedUserIDs(const QTreeWidget *treeWidget)
+{
+ if (!treeWidget) {
+ return {};
+ }
+
+ std::vector<GpgME::UserID> userIDs;
+ const auto selected = treeWidget->selectedItems();
+ std::transform(selected.begin(), selected.end(), std::back_inserter(userIDs), [](const QTreeWidgetItem *item) {
+ return item->data(0, Qt::UserRole).value<GpgME::UserID>();
+ });
+ return userIDs;
+}
+
+void addActionButton(QLayout *buttonBox, QAction *action)
+{
+ if (!action) {
+ return;
+ }
+ auto button = new QPushButton(buttonBox->parentWidget());
+ button->setText(action->text());
+ buttonBox->addWidget(button);
+ button->setEnabled(action->isEnabled());
+ QObject::connect(action, &QAction::changed, button, [action, button]() {
+ button->setEnabled(action->isEnabled());
+ });
+ QObject::connect(button, &QPushButton::clicked, action, &QAction::trigger);
+}
+}
+
+class UserIdsWidget::Private
+{
+public:
+ Private(UserIdsWidget *qq)
+ : q{qq}
+ {
+ }
+
+ TreeWidget *userIDTable = nullptr;
+ QPushButton *addUserIDBtn = nullptr;
+ QPushButton *setPrimaryUserIDBtn = nullptr;
+ QPushButton *certifyBtn = nullptr;
+ QPushButton *revokeCertificationsBtn = nullptr;
+ QPushButton *revokeUserIDBtn = nullptr;
+ GpgME::Key key;
+ bool updateInProgress = false;
+
+ QString trustLevelText(const GpgME::UserID &uid) const;
+ QIcon trustLevelIcon(const GpgME::UserID &uid) const;
+ QString tofuTooltipString(const GpgME::UserID &uid) const;
+ void setUpUserIDTable();
+ void addUserID();
+ void updateUserIDActions();
+ void setPrimaryUserID(const GpgME::UserID &uid = {});
+ void certifyUserIDs();
+ void revokeCertifications();
+ void revokeUserID(const GpgME::UserID &uid);
+ void revokeSelectedUserID();
+ void userIDTableContextMenuRequested(const QPoint &p);
+
+private:
+ UserIdsWidget *const q;
+};
+
+UserIdsWidget::UserIdsWidget(QWidget *parent)
+ : QWidget{parent}
+ , d{std::make_unique<Private>(this)}
+{
+ auto userIDsLayout = new QVBoxLayout{this};
+ userIDsLayout->setContentsMargins({});
+ userIDsLayout->setSpacing(0);
+
+ d->userIDTable = new TreeWidget{parent};
+ d->userIDTable->setAccessibleName(i18n("User IDs"));
+ QTreeWidgetItem *__qtreewidgetitem = new QTreeWidgetItem();
+ __qtreewidgetitem->setText(0, QString::fromUtf8("1"));
+ d->userIDTable->setHeaderItem(__qtreewidgetitem);
+ d->userIDTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ d->userIDTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ d->userIDTable->setRootIsDecorated(false);
+ d->userIDTable->setUniformRowHeights(true);
+ d->userIDTable->setAllColumnsShowFocus(false);
+
+ userIDsLayout->addWidget(d->userIDTable);
+
+ auto separator = new KSeparator(parent);
+ userIDsLayout->addWidget(separator);
+
+ auto buttonRow = new QHBoxLayout;
+
+ d->addUserIDBtn = new QPushButton(i18nc("@action:button", "Add User ID"), parent);
+ buttonRow->addWidget(d->addUserIDBtn);
+
+ d->setPrimaryUserIDBtn = new QPushButton{i18nc("@action:button", "Flag as Primary"), parent};
+ d->setPrimaryUserIDBtn->setToolTip(i18nc("@info:tooltip", "Flag the selected user ID as the primary user ID of this key."));
+ buttonRow->addWidget(d->setPrimaryUserIDBtn);
+
+ d->certifyBtn = new QPushButton(i18nc("@action:button", "Certify User IDs"), parent);
+ buttonRow->addWidget(d->certifyBtn);
+
+ d->revokeCertificationsBtn = new QPushButton(i18nc("@action:button", "Revoke Certifications"), parent);
+ buttonRow->addWidget(d->revokeCertificationsBtn);
+
+ d->revokeUserIDBtn = new QPushButton(i18nc("@action:button", "Revoke User ID"), parent);
+ buttonRow->addWidget(d->revokeUserIDBtn);
+
+ buttonRow->addStretch(1);
+
+ userIDsLayout->addLayout(buttonRow);
+ setLayout(userIDsLayout);
+
+ connect(d->addUserIDBtn, &QPushButton::clicked, this, [this]() {
+ d->addUserID();
+ });
+ connect(d->userIDTable, &QTreeWidget::itemSelectionChanged, this, [this]() {
+ d->updateUserIDActions();
+ });
+ connect(d->setPrimaryUserIDBtn, &QPushButton::clicked, this, [this]() {
+ d->setPrimaryUserID();
+ });
+ connect(d->certifyBtn, &QPushButton::clicked, this, [this]() {
+ d->certifyUserIDs();
+ });
+ connect(d->revokeCertificationsBtn, &QPushButton::clicked, this, [this]() {
+ d->revokeCertifications();
+ });
+ connect(d->revokeUserIDBtn, &QPushButton::clicked, this, [this]() {
+ d->revokeSelectedUserID();
+ });
+
+ d->userIDTable->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(d->userIDTable, &QAbstractItemView::customContextMenuRequested, this, [this](const QPoint &p) {
+ d->userIDTableContextMenuRequested(p);
+ });
+}
+
+void UserIdsWidget::Private::updateUserIDActions()
+{
+ const auto userIDs = selectedUserIDs(userIDTable);
+ const auto singleUserID = userIDs.size() == 1 ? userIDs.front() : GpgME::UserID{};
+ const bool isPrimaryUserID = !singleUserID.isNull() && (userIDTable->selectedItems().front() == userIDTable->topLevelItem(0));
+ setPrimaryUserIDBtn->setEnabled(!singleUserID.isNull() //
+ && !isPrimaryUserID //
+ && !Kleo::isRevokedOrExpired(singleUserID) //
+ && canBeUsedForSecretKeyOperations(key));
+ revokeUserIDBtn->setEnabled(!singleUserID.isNull() && canCreateCertifications(key) && canRevokeUserID(singleUserID));
+}
+
+UserIdsWidget::~UserIdsWidget() = default;
+
+GpgME::Key UserIdsWidget::key() const
+{
+ return d->key;
+}
+
+void UserIdsWidget::setKey(const GpgME::Key &key)
+{
+ d->key = key;
+ d->setUpUserIDTable();
+ const bool isOwnKey = key.hasSecret();
+ const auto isLocalKey = !isRemoteKey(key);
+ const auto keyCanBeCertified = Kleo::canBeCertified(key);
+ const auto userCanSignUserIDs = userHasCertificationKey();
+
+ d->addUserIDBtn->setVisible(isOwnKey);
+ d->addUserIDBtn->setEnabled(canBeUsedForSecretKeyOperations(key));
+ d->setPrimaryUserIDBtn->setVisible(isOwnKey);
+ d->setPrimaryUserIDBtn->setEnabled(false); // requires a selected user ID
+ d->certifyBtn->setVisible(true); // always visible (for OpenPGP keys)
+ d->certifyBtn->setEnabled(isLocalKey && keyCanBeCertified && userCanSignUserIDs);
+ d->revokeCertificationsBtn->setVisible(Kleo::Commands::RevokeCertificationCommand::isSupported());
+ d->revokeCertificationsBtn->setEnabled(userCanSignUserIDs && isLocalKey);
+ d->revokeUserIDBtn->setVisible(isOwnKey);
+ d->revokeUserIDBtn->setEnabled(false); // requires a selected user ID
+}
+
+void UserIdsWidget::Private::setUpUserIDTable()
+{
+ userIDTable->clear();
+
+ QStringList headers = {i18n("Email"), i18n("Name"), i18n("Trust Level"), i18n("Tags")};
+ userIDTable->setColumnCount(headers.count());
+ userIDTable->setColumnWidth(0, 200);
+ userIDTable->setColumnWidth(1, 200);
+ userIDTable->setHeaderLabels(headers);
+
+ const auto uids = key.userIDs();
+ for (unsigned int i = 0; i < uids.size(); ++i) {
+ const auto &uid = uids[i];
+ auto item = new QTreeWidgetItem;
+ const QString toolTip = tofuTooltipString(uid);
+ item->setData(0, Qt::UserRole, QVariant::fromValue(uid));
+
+ auto pMail = Kleo::Formatting::prettyEMail(uid);
+ auto pName = Kleo::Formatting::prettyName(uid);
+
+ item->setData(0, Qt::DisplayRole, pMail);
+ item->setData(0, Qt::ToolTipRole, toolTip);
+ item->setData(0, Qt::AccessibleTextRole, pMail.isEmpty() ? i18nc("text for screen readers for an empty email address", "no email") : pMail);
+ item->setData(1, Qt::DisplayRole, pName);
+ item->setData(1, Qt::ToolTipRole, toolTip);
+
+ item->setData(2, Qt::DecorationRole, trustLevelIcon(uid));
+ item->setData(2, Qt::DisplayRole, trustLevelText(uid));
+ item->setData(2, Qt::ToolTipRole, toolTip);
+
+ GpgME::Error err;
+ QStringList tagList;
+ for (const auto &tag : uid.remarks(Tags::tagKeys(), err)) {
+ if (err) {
+ qCWarning(KLEOPATRA_LOG) << "Getting remarks for user ID" << uid.id() << "failed:" << err;
+ }
+ tagList << QString::fromStdString(tag);
+ }
+ qCDebug(KLEOPATRA_LOG) << "tagList:" << tagList;
+ const auto tags = tagList.join(QStringLiteral("; "));
+ item->setData(3, Qt::DisplayRole, tags);
+ item->setData(3, Qt::ToolTipRole, toolTip);
+
+ userIDTable->addTopLevelItem(item);
+ }
+ userIDTable->restoreColumnLayout(QStringLiteral("UserIDTable"));
+ if (!Tags::tagsEnabled()) {
+ userIDTable->hideColumn(3);
+ }
+ for (int i = 0; i < userIDTable->columnCount(); i++) {
+ userIDTable->resizeColumnToContents(i);
+ }
+}
+
+QString UserIdsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const
+{
+ const auto tofu = uid.tofuInfo();
+ if (tofu.isNull()) {
+ return QString();
+ }
+
+ QString html = QStringLiteral("<table border=\"0\" cell-padding=\"5\">");
+ const auto appendRow = [&html](const QString &lbl, const QString &val) {
+ html += QStringLiteral(
+ "<tr>"
+ "<th style=\"text-align: right; padding-right: 5px; white-space: nowrap;\">%1:</th>"
+ "<td style=\"white-space: nowrap;\">%2</td>"
+ "</tr>")
+ .arg(lbl, val);
+ };
+ const auto appendHeader = [this, &html](const QString &hdr) {
+ html += QStringLiteral("<tr><th colspan=\"2\" style=\"background-color: %1; color: %2\">%3</th></tr>")
+ .arg(q->palette().highlight().color().name(), q->palette().highlightedText().color().name(), hdr);
+ };
+ const auto dateTime = [](long ts) {
+ QLocale l;
+ return ts == 0 ? i18n("never") : l.toString(QDateTime::fromSecsSinceEpoch(ts), QLocale::ShortFormat);
+ };
+ appendHeader(i18n("Signing"));
+ appendRow(i18n("First message"), dateTime(tofu.signFirst()));
+ appendRow(i18n("Last message"), dateTime(tofu.signLast()));
+ appendRow(i18n("Message count"), QString::number(tofu.signCount()));
+ appendHeader(i18n("Encryption"));
+ appendRow(i18n("First message"), dateTime(tofu.encrFirst()));
+ appendRow(i18n("Last message"), dateTime(tofu.encrLast()));
+ appendRow(i18n("Message count"), QString::number(tofu.encrCount()));
+
+ html += QStringLiteral("</table>");
+ // Make sure the tooltip string is different for each UserID, even if the
+ // data are the same, otherwise the tooltip is not updated and moved when
+ // user moves mouse from one row to another.
+ html += QStringLiteral("<!-- %1 //-->").arg(QString::fromUtf8(uid.id()));
+ return html;
+}
+
+QIcon UserIdsWidget::Private::trustLevelIcon(const GpgME::UserID &uid) const
+{
+ if (updateInProgress) {
+ return QIcon::fromTheme(QStringLiteral("emblem-question"));
+ }
+ switch (uid.validity()) {
+ case GpgME::UserID::Unknown:
+ case GpgME::UserID::Undefined:
+ return QIcon::fromTheme(QStringLiteral("emblem-question"));
+ case GpgME::UserID::Never:
+ return QIcon::fromTheme(QStringLiteral("emblem-error"));
+ case GpgME::UserID::Marginal:
+ return QIcon::fromTheme(QStringLiteral("emblem-warning"));
+ case GpgME::UserID::Full:
+ case GpgME::UserID::Ultimate:
+ return QIcon::fromTheme(QStringLiteral("emblem-success"));
+ }
+ return {};
+}
+
+QString UserIdsWidget::Private::trustLevelText(const GpgME::UserID &uid) const
+{
+ return updateInProgress ? i18n("Updating...") : Formatting::validityShort(uid);
+}
+
+void UserIdsWidget::Private::addUserID()
+{
+ auto cmd = new Kleo::Commands::AddUserIDCommand(key);
+ QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished, q, [this]() {
+ addUserIDBtn->setEnabled(true);
+ Q_EMIT q->updateKey();
+ });
+ addUserIDBtn->setEnabled(false);
+ cmd->start();
+}
+
+void UserIdsWidget::Private::setPrimaryUserID(const GpgME::UserID &uid)
+{
+ auto userId = uid;
+ if (userId.isNull()) {
+ const auto userIDs = selectedUserIDs(userIDTable);
+ if (userIDs.size() != 1) {
+ return;
+ }
+ userId = userIDs.front();
+ }
+
+ auto cmd = new Kleo::Commands::SetPrimaryUserIDCommand(userId);
+ connect(cmd, &Kleo::Commands::SetPrimaryUserIDCommand::finished, q, [this]() {
+ userIDTable->setEnabled(true);
+ // the Flag As Primary button will be updated by the key update
+ Q_EMIT q->updateKey();
+ });
+ userIDTable->setEnabled(false);
+ setPrimaryUserIDBtn->setEnabled(false);
+ cmd->start();
+}
+
+void UserIdsWidget::Private::certifyUserIDs()
+{
+ const auto userIDs = selectedUserIDs(userIDTable);
+ auto cmd = userIDs.empty() ? new Kleo::Commands::CertifyCertificateCommand{key} //
+ : new Kleo::Commands::CertifyCertificateCommand{userIDs};
+ connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() {
+ Q_EMIT q->updateKey();
+ certifyBtn->setEnabled(true);
+ });
+ certifyBtn->setEnabled(false);
+ cmd->start();
+}
+
+void UserIdsWidget::Private::revokeCertifications()
+{
+ const auto userIDs = selectedUserIDs(userIDTable);
+ auto cmd = userIDs.empty() ? new Kleo::Commands::RevokeCertificationCommand{key} //
+ : new Kleo::Commands::RevokeCertificationCommand{userIDs};
+ connect(cmd, &Kleo::Command::finished, q, [this]() {
+ Q_EMIT q->updateKey();
+ revokeCertificationsBtn->setEnabled(true);
+ });
+ revokeCertificationsBtn->setEnabled(false);
+ cmd->start();
+}
+
+void UserIdsWidget::Private::revokeUserID(const GpgME::UserID &userId)
+{
+ const QString message =
+ xi18nc("@info", "<para>Do you really want to revoke the user ID<nl/><emphasis>%1</emphasis> ?</para>", QString::fromUtf8(userId.id()));
+ auto confirmButton = KStandardGuiItem::ok();
+ confirmButton.setText(i18nc("@action:button", "Revoke User ID"));
+ confirmButton.setToolTip({});
+ const auto choice = KMessageBox::questionTwoActions(q->window(),
+ message,
+ i18nc("@title:window", "Confirm Revocation"),
+ confirmButton,
+ KStandardGuiItem::cancel(),
+ {},
+ KMessageBox::Notify | KMessageBox::WindowModal);
+ if (choice != KMessageBox::ButtonCode::PrimaryAction) {
+ return;
+ }
+
+ auto cmd = new Commands::RevokeUserIDCommand(userId);
+ cmd->setParentWidget(q);
+ connect(cmd, &Command::finished, q, [this]() {
+ userIDTable->setEnabled(true);
+ // the Revoke User ID button will be updated by the key update
+ Q_EMIT q->updateKey();
+ });
+ userIDTable->setEnabled(false);
+ revokeUserIDBtn->setEnabled(false);
+ cmd->start();
+}
+
+void UserIdsWidget::Private::revokeSelectedUserID()
+{
+ const auto userIDs = selectedUserIDs(userIDTable);
+ if (userIDs.size() != 1) {
+ return;
+ }
+ revokeUserID(userIDs.front());
+}
+
+void UserIdsWidget::Private::userIDTableContextMenuRequested(const QPoint &p)
+{
+ const auto userIDs = selectedUserIDs(userIDTable);
+ const auto singleUserID = (userIDs.size() == 1) ? userIDs.front() : GpgME::UserID{};
+ const bool isPrimaryUserID = !singleUserID.isNull() && (userIDTable->selectedItems().front() == userIDTable->topLevelItem(0));
+ const bool canSignUserIDs = userHasCertificationKey();
+ const auto isLocalKey = !isRemoteKey(key);
+ const auto keyCanBeCertified = Kleo::canBeCertified(key);
+
+ auto menu = new QMenu(q);
+ if (key.hasSecret()) {
+ auto action =
+ menu->addAction(QIcon::fromTheme(QStringLiteral("favorite")), i18nc("@action:inmenu", "Flag as Primary User ID"), q, [this, singleUserID]() {
+ setPrimaryUserID(singleUserID);
+ });
+ action->setEnabled(!singleUserID.isNull() //
+ && !isPrimaryUserID //
+ && !Kleo::isRevokedOrExpired(singleUserID) //
+ && canBeUsedForSecretKeyOperations(key));
+ }
+ {
+ const auto actionText = userIDs.empty() ? i18nc("@action:inmenu", "Certify User IDs...")
+ : i18ncp("@action:inmenu", "Certify User ID...", "Certify User IDs...", userIDs.size());
+ auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")), actionText, q, [this]() {
+ certifyUserIDs();
+ });
+ action->setEnabled(isLocalKey && keyCanBeCertified && canSignUserIDs);
+ }
+ if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
+ const auto actionText = userIDs.empty() ? i18nc("@action:inmenu", "Revoke Certifications...")
+ : i18ncp("@action:inmenu", "Revoke Certification...", "Revoke Certifications...", userIDs.size());
+ auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), actionText, q, [this]() {
+ revokeCertifications();
+ });
+ action->setEnabled(isLocalKey && canSignUserIDs);
+ }
+#ifdef MAILAKONADI_ENABLED
+ if (key.hasSecret()) {
+ auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")),
+ i18nc("@action:inmenu", "Publish at Mail Provider ..."),
+ q,
+ [this, singleUserID]() {
+ auto cmd = new Kleo::Commands::ExportOpenPGPCertToProviderCommand(singleUserID);
+ userIDTable->setEnabled(false);
+ connect(cmd, &Kleo::Commands::ExportOpenPGPCertToProviderCommand::finished, q, [this]() {
+ userIDTable->setEnabled(true);
+ });
+ cmd->start();
+ });
+ action->setEnabled(!singleUserID.isNull());
+ }
+#endif // MAILAKONADI_ENABLED
+ {
+ auto action =
+ menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), i18nc("@action:inmenu", "Revoke User ID"), q, [this, singleUserID]() {
+ revokeUserID(singleUserID);
+ });
+ action->setEnabled(!singleUserID.isNull() && canCreateCertifications(key) && canRevokeUserID(singleUserID));
+ }
+ connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
+ menu->popup(userIDTable->viewport()->mapToGlobal(p));
+}
+
+void UserIdsWidget::setUpdateInProgress(bool updateInProgress)
+{
+ d->updateInProgress = updateInProgress;
+}
+
+#include "moc_useridswidget.cpp"
diff --git a/src/dialogs/useridswidget.h b/src/dialogs/useridswidget.h
new file mode 100644
index 000000000..56151b496
--- /dev/null
+++ b/src/dialogs/useridswidget.h
@@ -0,0 +1,40 @@
+// 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 <QWidget>
+
+#include <memory>
+
+namespace GpgME
+{
+class Key;
+class KeyListResult;
+}
+
+namespace Kleo
+{
+
+class UserIdsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit UserIdsWidget(QWidget *parent = nullptr);
+ ~UserIdsWidget() override;
+
+ void setKey(const GpgME::Key &key);
+ GpgME::Key key() const;
+
+ void setUpdateInProgress(bool updateInProgress);
+
+Q_SIGNALS:
+ void updateKey();
+
+private:
+ class Private;
+ const std::unique_ptr<Private> d;
+};
+} // namespace Kleo
diff --git a/src/dialogs/weboftrustdialog.cpp b/src/dialogs/weboftrustdialog.cpp
deleted file mode 100644
index 65820e609..000000000
--- a/src/dialogs/weboftrustdialog.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/weboftrustdialog.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2017 Intevation GmbH
- SPDX-FileCopyrightText: 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 "weboftrustdialog.h"
-
-#include "weboftrustwidget.h"
-
-#include "commands/importcertificatefromkeyservercommand.h"
-
-#include <Libkleo/KeyHelpers>
-
-#include <QAction>
-#include <QDialogButtonBox>
-#include <QPushButton>
-#include <QVBoxLayout>
-
-#include <gpgme++/key.h>
-
-#include <KConfigGroup>
-#include <KLocalizedString>
-#include <KSharedConfig>
-
-#include <algorithm>
-#include <set>
-
-using namespace Kleo;
-
-namespace
-{
-void addActionButton(QDialogButtonBox *buttonBox, QAction *action)
-{
- if (!action) {
- return;
- }
- auto button = buttonBox->addButton(action->text(), QDialogButtonBox::ActionRole);
- button->setEnabled(action->isEnabled());
- QObject::connect(action, &QAction::changed, button, [action, button]() {
- button->setEnabled(action->isEnabled());
- });
- QObject::connect(button, &QPushButton::clicked, action, &QAction::trigger);
-}
-}
-
-WebOfTrustDialog::WebOfTrustDialog(QWidget *parent)
- : QDialog(parent)
-{
- KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("WebOfTrustDialog"));
- const QSize size = dialog.readEntry("Size", QSize(900, 400));
- if (size.isValid()) {
- resize(size);
- }
- setWindowTitle(i18nc("@title:window", "Certifications"));
-
- mWidget = new WebOfTrustWidget(this);
- auto l = new QVBoxLayout(this);
- l->addWidget(mWidget);
-
- auto bbox = new QDialogButtonBox(this);
-
- auto btn = bbox->addButton(QDialogButtonBox::Close);
- connect(btn, &QPushButton::pressed, this, &QDialog::accept);
-
- addActionButton(bbox, mWidget->detailsAction());
- addActionButton(bbox, mWidget->certifyAction());
- addActionButton(bbox, mWidget->revokeAction());
-
- mFetchKeysBtn = bbox->addButton(i18nc("@action:button", "Fetch Missing Keys"), QDialogButtonBox::ActionRole);
- mFetchKeysBtn->setToolTip(i18nc("@info:tooltip", "Look up and import all keys that were used to certify the user IDs of this key"));
- connect(mFetchKeysBtn, &QPushButton::pressed, this, &WebOfTrustDialog::fetchMissingKeys);
-
- l->addWidget(bbox);
-}
-
-void WebOfTrustDialog::setKey(const GpgME::Key &key)
-{
- mWidget->setKey(key);
- mFetchKeysBtn->setEnabled(!key.isBad());
-}
-
-GpgME::Key WebOfTrustDialog::key() const
-{
- return mWidget->key();
-}
-
-WebOfTrustDialog::~WebOfTrustDialog()
-{
- KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("WebOfTrustDialog"));
- dialog.writeEntry("Size", size());
- dialog.sync();
-}
-
-void WebOfTrustDialog::fetchMissingKeys()
-{
- if (key().isNull()) {
- return;
- }
- const auto missingSignerKeyIds = Kleo::getMissingSignerKeyIds(key().userIDs());
-
- auto cmd = new Kleo::ImportCertificateFromKeyserverCommand{QStringList{std::begin(missingSignerKeyIds), std::end(missingSignerKeyIds)}};
- cmd->setParentWidget(this);
- mFetchKeysBtn->setEnabled(false);
- connect(cmd, &Kleo::ImportCertificateFromKeyserverCommand::finished, this, [this]() {
- // Trigger an update when done
- setKey(key());
- mFetchKeysBtn->setEnabled(true);
- });
- cmd->start();
-}
-
-#include "moc_weboftrustdialog.cpp"
diff --git a/src/dialogs/weboftrustdialog.h b/src/dialogs/weboftrustdialog.h
deleted file mode 100644
index cc752a1ae..000000000
--- a/src/dialogs/weboftrustdialog.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/weboftrustdialog.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2017 Intevation GmbH
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <QDialog>
-#include <QWidget>
-
-namespace GpgME
-{
-class Key;
-}
-
-namespace Kleo
-{
-class WebOfTrustWidget;
-
-class WebOfTrustDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit WebOfTrustDialog(QWidget *parent = nullptr);
- ~WebOfTrustDialog() override;
-
- void setKey(const GpgME::Key &key);
- GpgME::Key key() const;
-
-private:
- void fetchMissingKeys();
-
-private:
- QPushButton *mFetchKeysBtn = nullptr;
- WebOfTrustWidget *mWidget = nullptr;
-};
-
-} // namespace Kleo
diff --git a/src/dialogs/weboftrustwidget.cpp b/src/dialogs/weboftrustwidget.cpp
index bc8659980..7a22a0e1f 100644
--- a/src/dialogs/weboftrustwidget.cpp
+++ b/src/dialogs/weboftrustwidget.cpp
@@ -1,347 +1,411 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 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
*/
#include "weboftrustwidget.h"
#include "commands/certifycertificatecommand.h"
+#include "commands/importcertificatefromkeyservercommand.h"
#include "commands/revokecertificationcommand.h"
#include "utils/tags.h"
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/TreeView>
#include <Libkleo/UserIDListModel>
#include <KLocalizedString>
#include <KMessageBox>
+#include <KSeparator>
+#include <QDialogButtonBox>
#include <QHeaderView>
#include <QMenu>
+#include <QPushButton>
#include <QVBoxLayout>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include "kleopatra_debug.h"
using namespace Kleo;
+namespace
+{
+void addActionButton(QLayout *buttonBox, QAction *action)
+{
+ if (!action) {
+ return;
+ }
+ auto button = new QPushButton(buttonBox->parentWidget());
+ button->setText(action->text());
+ buttonBox->addWidget(button);
+ button->setEnabled(action->isEnabled());
+ QObject::connect(action, &QAction::changed, button, [action, button]() {
+ button->setEnabled(action->isEnabled());
+ });
+ QObject::connect(button, &QPushButton::clicked, action, &QAction::trigger);
+}
+}
+
class WebOfTrustWidget::Private
{
friend class ::Kleo::WebOfTrustWidget;
WebOfTrustWidget *const q;
private:
GpgME::Key key;
UserIDListModel certificationsModel;
QGpgME::KeyListJob *keyListJob = nullptr;
TreeView *certificationsTV = nullptr;
QAction *detailsAction = nullptr;
QAction *certifyAction = nullptr;
QAction *revokeAction = nullptr;
+ QPushButton *fetchBtn = nullptr;
public:
Private(WebOfTrustWidget *qq)
: q{qq}
{
certificationsModel.enableRemarks(Tags::tagsEnabled());
+ auto vLay = new QVBoxLayout(q);
+ vLay->setContentsMargins({});
+ vLay->setSpacing(0);
certificationsTV = new TreeView{q};
certificationsTV->setAccessibleName(i18n("User IDs and certifications"));
certificationsTV->setModel(&certificationsModel);
certificationsTV->setAllColumnsShowFocus(false);
certificationsTV->setSelectionMode(QAbstractItemView::SingleSelection);
if (!Tags::tagsEnabled()) {
certificationsTV->hideColumn(static_cast<int>(UserIDListModel::Column::Tags));
}
-
- auto vLay = new QVBoxLayout(q);
- vLay->setContentsMargins(0, 0, 0, 0);
vLay->addWidget(certificationsTV);
detailsAction = new QAction{QIcon::fromTheme(QStringLiteral("dialog-information")), i18nc("@action", "Show Certificate Details"), q};
connect(detailsAction, &QAction::triggered, q, [this]() {
showCertificateDetails();
});
certifyAction = new QAction{QIcon::fromTheme(QStringLiteral("view-certificate-sign")), i18nc("@action", "Add Certification"), q};
connect(certifyAction, &QAction::triggered, q, [this]() {
addCertification();
});
if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
revokeAction = new QAction{QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), i18nc("@action", "Revoke Certification"), q};
connect(revokeAction, &QAction::triggered, q, [this]() {
revokeCertification();
});
}
+ auto separator = new KSeparator(q);
+ vLay->addWidget(separator);
+
+ auto bbox = new QHBoxLayout;
+
+ addActionButton(bbox, detailsAction);
+ addActionButton(bbox, certifyAction);
+ addActionButton(bbox, revokeAction);
+
+ fetchBtn = new QPushButton(i18nc("@action:button", "Fetch Missing Keys"));
+ bbox->addWidget(fetchBtn);
+ fetchBtn->setToolTip(i18nc("@info:tooltip", "Look up and import all keys that were used to certify the user IDs of this key"));
+ connect(fetchBtn, &QPushButton::pressed, q, [this]() {
+ fetchMissingKeys();
+ });
+
+ bbox->addStretch(1);
+
+ vLay->addLayout(bbox);
connect(certificationsTV, &QAbstractItemView::doubleClicked, q, [this]() {
certificationDblClicked();
});
certificationsTV->setContextMenuPolicy(Qt::CustomContextMenu);
connect(certificationsTV, &QWidget::customContextMenuRequested, q, [this](const QPoint &p) {
contextMenuRequested(p);
});
connect(certificationsTV->selectionModel(), &QItemSelectionModel::currentRowChanged, q, [this]() {
updateActions();
});
updateActions();
}
GpgME::UserID selectedUserID()
{
return certificationsModel.userID(certificationsTV->currentIndex());
}
GpgME::UserID::Signature selectedCertification()
{
return certificationsModel.signature(certificationsTV->currentIndex());
}
void certificationDblClicked()
{
showCertificateDetails();
}
void showCertificateDetails()
{
const auto signature = selectedCertification();
if (signature.isNull()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no certification selected";
return;
}
auto cmd = Command::commandForQuery(QString::fromUtf8(signature.signerKeyID()));
cmd->setParentWId(q->winId());
cmd->start();
}
void addCertification()
{
auto userID = selectedUserID();
if (userID.isNull()) {
userID = selectedCertification().parent();
}
if (userID.isNull()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no user ID or certification selected";
return;
}
auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID);
cmd->setParentWidget(q);
certificationsTV->setEnabled(false);
connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() {
certificationsTV->setEnabled(true);
// Trigger an update when done
q->setKey(key);
});
cmd->start();
}
void revokeCertification()
{
Command *cmd = nullptr;
if (const auto signature = selectedCertification(); !signature.isNull()) {
cmd = new Kleo::Commands::RevokeCertificationCommand(signature);
} else if (const auto userID = selectedUserID(); !userID.isNull()) {
cmd = new Kleo::Commands::RevokeCertificationCommand(userID);
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no user ID or certification selected";
return;
}
cmd->setParentWidget(q);
certificationsTV->setEnabled(false);
connect(cmd, &Kleo::Commands::RevokeCertificationCommand::finished, q, [this]() {
certificationsTV->setEnabled(true);
// Trigger an update when done
q->setKey(key);
});
cmd->start();
}
void addActionsForUserID(QMenu *menu)
{
menu->addAction(certifyAction);
if (revokeAction) {
menu->addAction(revokeAction);
}
}
void addActionsForSignature(QMenu *menu)
{
menu->addAction(detailsAction);
menu->addAction(certifyAction);
if (revokeAction) {
menu->addAction(revokeAction);
if (!revokeAction->isEnabled()) {
menu->setToolTipsVisible(true);
}
}
}
void updateActions()
{
const auto userCanSignUserIDs = userHasCertificationKey();
const auto keyCanBeCertified = Kleo::canBeCertified(key);
const auto userID = selectedUserID();
const auto signature = selectedCertification();
detailsAction->setEnabled(!signature.isNull());
certifyAction->setEnabled(keyCanBeCertified && userCanSignUserIDs && (!userID.isNull() || !signature.isNull()));
if (revokeAction) {
revokeAction->setToolTip({});
if (!signature.isNull()) {
const auto revocationFeasibility = userCanRevokeCertification(signature);
revokeAction->setEnabled(revocationFeasibility == CertificationCanBeRevoked);
switch (revocationFeasibility) {
case CertificationCanBeRevoked:
break;
case CertificationNotMadeWithOwnKey:
revokeAction->setToolTip(
i18n("You cannot revoke this certification because it wasn't made with one of your keys (or the required secret key is missing)."));
break;
case CertificationIsSelfSignature:
revokeAction->setToolTip(i18n("Revocation of self-certifications is currently not possible."));
break;
case CertificationIsRevocation:
revokeAction->setToolTip(i18n("You cannot revoke this revocation certification. (But you can re-certify the corresponding user ID.)"));
break;
case CertificationIsExpired:
revokeAction->setToolTip(i18n("You cannot revoke this expired certification."));
break;
case CertificationIsInvalid:
revokeAction->setToolTip(i18n("You cannot revoke this invalid certification."));
break;
case CertificationKeyNotAvailable:
revokeAction->setToolTip(i18n("You cannot revoke this certification because the required secret key is not available."));
break;
};
} else if (!userID.isNull()) {
const bool canRevokeCertification = userCanRevokeCertifications(userID);
revokeAction->setEnabled(canRevokeCertification);
if (!canRevokeCertification) {
revokeAction->setToolTip(
i18n("You cannot revoke any of the certifications of this user ID. Select any of the certifications for details."));
}
} else {
revokeAction->setEnabled(false);
}
}
}
void contextMenuRequested(const QPoint &p)
{
const auto index = certificationsTV->indexAt(p);
const auto userID = certificationsModel.userID(index);
const auto signature = certificationsModel.signature(index);
if (userID.isNull() && signature.isNull()) {
return;
}
auto menu = new QMenu(q);
if (!userID.isNull()) {
addActionsForUserID(menu);
} else if (!signature.isNull()) {
addActionsForSignature(menu);
}
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
menu->popup(certificationsTV->viewport()->mapToGlobal(p));
}
void startSignatureListing()
{
if (keyListJob) {
return;
}
QGpgME::KeyListJob *const job = QGpgME::openpgp()->keyListJob(/*remote*/ false, /*includeSigs*/ true, /*validate*/ true);
if (!job) {
return;
}
if (Tags::tagsEnabled()) {
job->addMode(GpgME::SignatureNotations);
}
connect(job, &QGpgME::KeyListJob::result, q, &WebOfTrustWidget::signatureListingDone);
connect(job, &QGpgME::KeyListJob::nextKey, q, &WebOfTrustWidget::signatureListingNextKey);
job->start(QStringList(QString::fromLatin1(key.primaryFingerprint())));
keyListJob = job;
}
+
+ void fetchMissingKeys()
+ {
+ if (q->key().isNull()) {
+ return;
+ }
+ const auto missingSignerKeyIds = Kleo::getMissingSignerKeyIds(q->key().userIDs());
+
+ auto cmd = new Kleo::ImportCertificateFromKeyserverCommand{QStringList{std::begin(missingSignerKeyIds), std::end(missingSignerKeyIds)}};
+ cmd->setParentWidget(q);
+ fetchBtn->setEnabled(false);
+ connect(cmd, &Kleo::ImportCertificateFromKeyserverCommand::finished, q, [this]() {
+ // Trigger an update when done
+ q->setKey(q->key());
+ fetchBtn->setEnabled(true);
+ });
+ cmd->start();
+ }
};
WebOfTrustWidget::WebOfTrustWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
WebOfTrustWidget::~WebOfTrustWidget() = default;
QAction *WebOfTrustWidget::detailsAction() const
{
return d->detailsAction;
}
QAction *WebOfTrustWidget::certifyAction() const
{
return d->certifyAction;
}
QAction *WebOfTrustWidget::revokeAction() const
{
return d->revokeAction;
}
GpgME::Key WebOfTrustWidget::key() const
{
return d->key;
}
void WebOfTrustWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::OpenPGP) {
qCDebug(KLEOPATRA_LOG) << "List of Certifications is only supported for OpenPGP keys";
return;
}
d->key = key;
d->certificationsModel.setKey(key);
d->updateActions();
d->certificationsTV->expandAll();
d->certificationsTV->header()->resizeSections(QHeaderView::ResizeToContents);
d->startSignatureListing();
d->certificationsTV->restoreColumnLayout(QStringLiteral("WebOfTrustWidget"));
+ for (int i = 0; i < d->certificationsModel.columnCount(); i++) {
+ d->certificationsTV->resizeColumnToContents(i);
+ }
+ d->fetchBtn->setEnabled(!key.isBad());
}
void WebOfTrustWidget::signatureListingNextKey(const GpgME::Key &key)
{
GpgME::Key merged = key;
merged.mergeWith(d->key);
setKey(merged);
}
void WebOfTrustWidget::signatureListingDone(const GpgME::KeyListResult &result)
{
if (result.error()) {
KMessageBox::information(this,
xi18nc("@info",
"<para>An error occurred while loading the certifications: "
"<message>%1</message></para>",
Formatting::errorAsString(result.error())),
i18nc("@title", "Certifications Loading Failed"));
}
d->keyListJob = nullptr;
}
#include "moc_weboftrustwidget.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 30, 5:31 PM (20 h, 58 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
aa/69/1ed21668b5e08ecb36e5f0f1626d

Event Timeline