Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34328972
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
142 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment