Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5fb4d5088..6775d7de2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,640 +1,639 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(icons)
add_subdirectory(mimetypes)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if (NOT DISABLE_KWATCHGNUPG)
add_subdirectory(kwatchgnupg)
endif()
add_subdirectory(libkleopatraclient)
add_subdirectory(conf)
if(WIN32)
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp)
set(_kleopatra_extra_SRCS
selftest/registrycheck.cpp selftest/registrycheck.h
utils/gnupg-registry.c
utils/userinfo_win.cpp
utils/winapi-helpers.cpp utils/winapi-helpers.h
utils/windowsprocessdevice.cpp utils/windowsprocessdevice.h
versioninfo.rc kleopatra.w32-manifest
)
else()
set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp)
set(_kleopatra_extra_SRCS)
endif()
set(_kleopatra_uiserver_SRCS
${_kleopatra_extra_uiserver_SRCS}
selftest/uiservercheck.cpp selftest/uiservercheck.h
uiserver/assuanserverconnection.cpp uiserver/assuanserverconnection.h
uiserver/createchecksumscommand.cpp uiserver/createchecksumscommand.h
uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandemailbase.h
uiserver/decryptverifycommandfilesbase.cpp uiserver/decryptverifycommandfilesbase.h
uiserver/echocommand.cpp uiserver/echocommand.h
uiserver/encryptcommand.cpp uiserver/encryptcommand.h
uiserver/importfilescommand.cpp uiserver/importfilescommand.h
uiserver/prepencryptcommand.cpp uiserver/prepencryptcommand.h
uiserver/prepsigncommand.cpp uiserver/prepsigncommand.h
uiserver/selectcertificatecommand.cpp
uiserver/sessiondata.cpp uiserver/sessiondata.h
uiserver/signcommand.cpp uiserver/signcommand.h
uiserver/signencryptfilescommand.cpp
uiserver/uiserver.cpp
uiserver/verifychecksumscommand.cpp uiserver/verifychecksumscommand.h
)
set(_kleopatra_uiserver_extra_libs LibAssuan::LibAssuan LibGpgError::LibGpgError)
if(HAVE_GPG_ERR_SOURCE_KLEO)
add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
else()
add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
endif()
if(KPim6IdentityManagementCore_FOUND AND KPim6MailTransport_FOUND AND KPim6AkonadiMime_FOUND)
set(_kleopatra_mail_libs
KPim6::IdentityManagementCore # Export OpenPGP keys using WKS
KPim6::MailTransport
KPim6::AkonadiMime
)
add_definitions(-DMAILAKONADI_ENABLED)
endif()
ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
set(_kleopatra_SRCS
${_kleopatra_extra_SRCS}
accessibility/accessiblelink.cpp
accessibility/accessiblelink_p.h
accessibility/accessiblerichtextlabel.cpp
accessibility/accessiblerichtextlabel_p.h
accessibility/accessiblevaluelabel.cpp
accessibility/accessiblevaluelabel_p.h
accessibility/accessiblewidgetfactory.cpp
accessibility/accessiblewidgetfactory.h
commands/addadskcommand.cpp
commands/addadskcommand.h
commands/addsubkeycommand.cpp
commands/addsubkeycommand.h
commands/adduseridcommand.cpp
commands/adduseridcommand.h
commands/authenticatepivcardapplicationcommand.cpp
commands/authenticatepivcardapplicationcommand.h
commands/cardcommand.cpp
commands/cardcommand.h
commands/certificatetocardcommand.cpp
commands/certificatetocardcommand.h
commands/certificatetopivcardcommand.cpp
commands/certificatetopivcardcommand.h
commands/certifycertificatecommand.cpp
commands/certifycertificatecommand.h
commands/certifygroupcommand.cpp
commands/certifygroupcommand.h
commands/changeexpirycommand.cpp
commands/changeexpirycommand.h
commands/changeownertrustcommand.cpp
commands/changeownertrustcommand.h
commands/changepassphrasecommand.cpp
commands/changepassphrasecommand.h
commands/changepincommand.cpp
commands/changepincommand.h
commands/changeroottrustcommand.cpp
commands/changeroottrustcommand.h
commands/checksumcreatefilescommand.cpp
commands/checksumcreatefilescommand.h
commands/checksumverifyfilescommand.cpp
commands/checksumverifyfilescommand.h
commands/clearcrlcachecommand.cpp
commands/clearcrlcachecommand.h
commands/command.cpp
commands/command.h
commands/createcsrforcardkeycommand.cpp
commands/createcsrforcardkeycommand.h
commands/creategroupcommand.cpp
commands/creategroupcommand.h
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.h
commands/decryptverifyclipboardcommand.cpp
commands/decryptverifyclipboardcommand.h
commands/decryptverifyfilescommand.cpp
commands/decryptverifyfilescommand.h
commands/deletecertificatescommand.cpp
commands/deletecertificatescommand.h
commands/detailscommand.cpp
commands/detailscommand.h
commands/dumpcertificatecommand.cpp
commands/dumpcertificatecommand.h
commands/dumpcrlcachecommand.cpp
commands/dumpcrlcachecommand.h
commands/encryptclipboardcommand.cpp
commands/encryptclipboardcommand.h
commands/exportcertificatecommand.cpp
commands/exportcertificatecommand.h
commands/exportgroupscommand.cpp
commands/exportgroupscommand.h
commands/exportopenpgpcertstoservercommand.cpp
commands/exportopenpgpcertstoservercommand.h
commands/exportopenpgpcerttoprovidercommand.cpp
commands/exportopenpgpcerttoprovidercommand.h
commands/exportpaperkeycommand.cpp
commands/exportpaperkeycommand.h
commands/exportsecretkeycommand.cpp
commands/exportsecretkeycommand.h
commands/exportsecretsubkeycommand.cpp
commands/exportsecretsubkeycommand.h
commands/genrevokecommand.cpp
commands/genrevokecommand.h
commands/gnupgprocesscommand.cpp
commands/gnupgprocesscommand.h
commands/importcertificatefromclipboardcommand.cpp
commands/importcertificatefromclipboardcommand.h
commands/importcertificatefromdatacommand.cpp
commands/importcertificatefromdatacommand.h
commands/importcertificatefromfilecommand.cpp
commands/importcertificatefromfilecommand.h
commands/importcertificatefromkeyservercommand.cpp
commands/importcertificatefromkeyservercommand.h
commands/importcertificatefrompivcardcommand.cpp
commands/importcertificatefrompivcardcommand.h
commands/importcertificatescommand.cpp
commands/importcertificatescommand.h
commands/importcrlcommand.cpp
commands/importcrlcommand.h
commands/importpaperkeycommand.cpp
commands/importpaperkeycommand.h
commands/keytocardcommand.cpp
commands/keytocardcommand.h
commands/lookupcertificatescommand.cpp
commands/lookupcertificatescommand.h
commands/newcertificatesigningrequestcommand.cpp
commands/newcertificatesigningrequestcommand.h
commands/newopenpgpcertificatecommand.cpp
commands/newopenpgpcertificatecommand.h
commands/openpgpgeneratecardkeycommand.cpp
commands/openpgpgeneratecardkeycommand.h
commands/pivgeneratecardkeycommand.cpp
commands/pivgeneratecardkeycommand.h
commands/refreshcertificatescommand.cpp
commands/refreshcertificatescommand.h
commands/refreshopenpgpcertscommand.cpp
commands/refreshopenpgpcertscommand.h
commands/refreshx509certscommand.cpp
commands/refreshx509certscommand.h
commands/reloadkeyscommand.cpp
commands/reloadkeyscommand.h
commands/revokecertificationcommand.cpp
commands/revokecertificationcommand.h
commands/revokekeycommand.cpp
commands/revokekeycommand.h
commands/revokeuseridcommand.cpp
commands/revokeuseridcommand.h
commands/selftestcommand.cpp
commands/selftestcommand.h
commands/setinitialpincommand.cpp
commands/setinitialpincommand.h
commands/setpivcardapplicationadministrationkeycommand.cpp
commands/setpivcardapplicationadministrationkeycommand.h
commands/setprimaryuseridcommand.cpp
commands/setprimaryuseridcommand.h
commands/signclipboardcommand.cpp
commands/signclipboardcommand.h
commands/signencryptfilescommand.cpp
commands/signencryptfilescommand.h
commands/signencryptfoldercommand.cpp
commands/signencryptfoldercommand.h
commands/viewemailfilescommand.cpp
commands/viewemailfilescommand.h
conf/configuredialog.cpp
conf/configuredialog.h
conf/groupsconfigdialog.cpp
conf/groupsconfigdialog.h
conf/groupsconfigwidget.cpp
conf/groupsconfigwidget.h
crypto/autodecryptverifyfilescontroller.cpp
crypto/autodecryptverifyfilescontroller.h
crypto/certificateresolver.cpp
crypto/certificateresolver.h
crypto/checksumsutils_p.cpp
crypto/checksumsutils_p.h
crypto/controller.cpp
crypto/controller.h
crypto/createchecksumscontroller.cpp
crypto/createchecksumscontroller.h
crypto/decryptverifyemailcontroller.cpp
crypto/decryptverifyemailcontroller.h
crypto/decryptverifyfilescontroller.cpp
crypto/decryptverifyfilescontroller.h
crypto/decryptverifytask.cpp
crypto/decryptverifytask.h
crypto/encryptemailcontroller.cpp
crypto/encryptemailcontroller.h
crypto/encryptemailtask.cpp
crypto/encryptemailtask.h
crypto/gui/certificatelineedit.cpp
crypto/gui/certificatelineedit.h
crypto/gui/certificateselectionline.cpp
crypto/gui/certificateselectionline.h
crypto/gui/decryptverifyfilesdialog.cpp
crypto/gui/decryptverifyfilesdialog.h
crypto/gui/decryptverifyfileswizard.cpp
crypto/gui/decryptverifyfileswizard.h
crypto/gui/decryptverifyoperationwidget.cpp
crypto/gui/decryptverifyoperationwidget.h
crypto/gui/encryptemailwizard.cpp
crypto/gui/encryptemailwizard.h
crypto/gui/newresultpage.cpp
crypto/gui/newresultpage.h
crypto/gui/objectspage.cpp
crypto/gui/objectspage.h
crypto/gui/resolverecipientspage.cpp
crypto/gui/resolverecipientspage.h
crypto/gui/resultitemwidget.cpp
crypto/gui/resultitemwidget.h
crypto/gui/resultlistwidget.cpp
crypto/gui/resultlistwidget.h
crypto/gui/resultpage.cpp
crypto/gui/resultpage.h
crypto/gui/signemailwizard.cpp
crypto/gui/signemailwizard.h
crypto/gui/signencryptemailconflictdialog.cpp
crypto/gui/signencryptemailconflictdialog.h
crypto/gui/signencryptfileswizard.cpp
crypto/gui/signencryptfileswizard.h
crypto/gui/signencryptwidget.cpp
crypto/gui/signencryptwidget.h
crypto/gui/signencryptwizard.cpp
crypto/gui/signencryptwizard.h
crypto/gui/signerresolvepage.cpp
crypto/gui/signerresolvepage.h
crypto/gui/signingcertificateselectiondialog.cpp
crypto/gui/signingcertificateselectiondialog.h
crypto/gui/signingcertificateselectionwidget.cpp
crypto/gui/signingcertificateselectionwidget.h
crypto/gui/unknownrecipientwidget.cpp
crypto/gui/unknownrecipientwidget.h
crypto/gui/verifychecksumsdialog.cpp
crypto/gui/verifychecksumsdialog.h
crypto/gui/wizard.cpp
crypto/gui/wizard.h
crypto/gui/wizardpage.cpp
crypto/gui/wizardpage.h
crypto/newsignencryptemailcontroller.cpp
crypto/newsignencryptemailcontroller.h
crypto/recipient.cpp
crypto/recipient.h
crypto/sender.cpp
crypto/sender.h
crypto/signemailcontroller.cpp
crypto/signemailcontroller.h
crypto/signemailtask.cpp
crypto/signemailtask.h
crypto/signencryptfilescontroller.cpp
crypto/signencryptfilescontroller.h
crypto/signencrypttask.cpp
crypto/signencrypttask.h
crypto/task.cpp
crypto/task.h
crypto/taskcollection.cpp
crypto/taskcollection.h
crypto/verifychecksumscontroller.cpp
crypto/verifychecksumscontroller.h
dialogs/addsubkeydialog.cpp
dialogs/addsubkeydialog.h
dialogs/adduseriddialog.cpp
dialogs/adduseriddialog.h
dialogs/animatedexpander.cpp
dialogs/animatedexpander.h
dialogs/cardinfotab.cpp
dialogs/cardinfotab.h
dialogs/certificatedetailsdialog.cpp
dialogs/certificatedetailsdialog.h
dialogs/certificatedetailsinputwidget.cpp
dialogs/certificatedetailsinputwidget.h
dialogs/certificatedetailswidget.cpp
dialogs/certificatedetailswidget.h
dialogs/certificatedumpwidget.cpp
dialogs/certificatedumpwidget.h
dialogs/certificateselectiondialog.cpp
dialogs/certificateselectiondialog.h
dialogs/certifycertificatedialog.cpp
dialogs/certifycertificatedialog.h
dialogs/certifywidget.cpp
dialogs/certifywidget.h
dialogs/createcsrforcardkeydialog.cpp
dialogs/createcsrforcardkeydialog.h
dialogs/copytosmartcarddialog.cpp
dialogs/copytosmartcarddialog.h
dialogs/debugdialog.cpp
dialogs/debugdialog.h
dialogs/deletecertificatesdialog.cpp
dialogs/deletecertificatesdialog.h
dialogs/editgroupdialog.cpp
dialogs/editgroupdialog.h
dialogs/expirydialog.cpp
dialogs/expirydialog.h
dialogs/exportdialog.cpp
dialogs/exportdialog.h
dialogs/gencardkeydialog.cpp
dialogs/gencardkeydialog.h
dialogs/groupdetailsdialog.cpp
dialogs/groupdetailsdialog.h
dialogs/lookupcertificatesdialog.cpp
dialogs/lookupcertificatesdialog.h
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.h
dialogs/revokekeydialog.cpp
dialogs/revokekeydialog.h
dialogs/revokerswidget.cpp
dialogs/revokerswidget.h
dialogs/selftestdialog.cpp
dialogs/selftestdialog.h
dialogs/setinitialpindialog.cpp
dialogs/setinitialpindialog.h
dialogs/smartcardwindow.cpp
dialogs/smartcardwindow.h
dialogs/subkeyswidget.cpp
dialogs/subkeyswidget.h
dialogs/trustchainwidget.cpp
dialogs/trustchainwidget.h
dialogs/updatenotification.cpp
dialogs/updatenotification.h
dialogs/useridswidget.cpp
dialogs/useridswidget.h
dialogs/weboftrustwidget.cpp
dialogs/weboftrustwidget.h
interfaces/anchorprovider.h
interfaces/focusfirstchild.h
newcertificatewizard/advancedsettingsdialog.cpp
newcertificatewizard/advancedsettingsdialog_p.h
newcertificatewizard/enterdetailspage.cpp
newcertificatewizard/enterdetailspage_p.h
newcertificatewizard/keycreationpage.cpp
newcertificatewizard/keycreationpage_p.h
newcertificatewizard/listwidget.cpp
newcertificatewizard/listwidget.h
newcertificatewizard/newcertificatewizard.cpp
newcertificatewizard/newcertificatewizard.h
newcertificatewizard/resultpage.cpp
newcertificatewizard/resultpage_p.h
newcertificatewizard/wizardpage.cpp
newcertificatewizard/wizardpage_p.h
selftest/compliancecheck.cpp
selftest/compliancecheck.h
selftest/enginecheck.cpp
selftest/enginecheck.h
selftest/gpgagentcheck.cpp
selftest/gpgagentcheck.h
selftest/gpgconfcheck.cpp
selftest/gpgconfcheck.h
selftest/libkleopatrarccheck.cpp
selftest/libkleopatrarccheck.h
selftest/selftest.cpp
selftest/selftest.h
smartcard/algorithminfo.h
smartcard/card.cpp
smartcard/card.h
smartcard/deviceinfowatcher.cpp
smartcard/deviceinfowatcher.h
smartcard/keypairinfo.cpp
smartcard/keypairinfo.h
smartcard/netkeycard.cpp
smartcard/netkeycard.h
smartcard/openpgpcard.cpp
smartcard/openpgpcard.h
smartcard/p15card.cpp
smartcard/p15card.h
smartcard/pivcard.cpp
smartcard/pivcard.h
smartcard/readerstatus.cpp
smartcard/readerstatus.h
smartcard/utils.cpp
smartcard/utils.h
utils/accessibility.cpp
utils/accessibility.h
utils/action_data.cpp
utils/action_data.h
utils/applicationstate.cpp
utils/applicationstate.h
utils/archivedefinition.cpp
utils/archivedefinition.h
utils/certificatepair.h
utils/clipboardmenu.cpp
utils/clipboardmenu.h
utils/debug-helpers.cpp
utils/debug-helpers.h
utils/dragqueen.cpp
utils/dragqueen.h
utils/email.cpp
utils/email.h
utils/emptypassphraseprovider.cpp
utils/emptypassphraseprovider.h
utils/filedialog.cpp
utils/filedialog.h
utils/gui-helper.cpp
utils/gui-helper.h
utils/headerview.cpp
utils/headerview.h
utils/input.cpp
utils/input.h
utils/iodevicelogger.cpp
utils/iodevicelogger.h
utils/kdpipeiodevice.cpp
utils/kdpipeiodevice.h
utils/keyexportdraghandler.cpp
utils/keyexportdraghandler.h
utils/kuniqueservice.cpp
utils/kuniqueservice.h
utils/log.cpp
utils/log.h
utils/memory-helpers.h
utils/migration.cpp
utils/migration.h
utils/output.cpp
utils/output.h
utils/overwritedialog.cpp
utils/overwritedialog.h
utils/path-helper.cpp
utils/path-helper.h
utils/scrollarea.cpp
utils/scrollarea.h
utils/systemtrayicon.cpp
utils/systemtrayicon.h
utils/tags.cpp
utils/tags.h
utils/types.cpp
utils/types.h
utils/userinfo.cpp
utils/userinfo.h
utils/writecertassuantransaction.cpp
utils/writecertassuantransaction.h
utils/wsastarter.cpp
utils/wsastarter.h
view/anchorcache.cpp
view/anchorcache_p.h
view/cardkeysview.cpp
view/cardkeysview.h
view/htmllabel.cpp
view/htmllabel.h
view/infofield.cpp
view/infofield.h
view/keycacheoverlay.cpp
view/keycacheoverlay.h
view/keylistcontroller.cpp
view/keylistcontroller.h
view/keytreeview.cpp
view/keytreeview.h
view/netkeywidget.cpp
view/netkeywidget.h
view/nullpinwidget.cpp
view/nullpinwidget.h
view/openpgpkeycardwidget.cpp
view/openpgpkeycardwidget.h
view/overlaywidget.cpp
view/overlaywidget.h
view/p15cardwidget.cpp
view/p15cardwidget.h
view/padwidget.cpp
view/padwidget.h
view/pgpcardwidget.cpp
view/pgpcardwidget.h
view/pivcardwidget.cpp
view/pivcardwidget.h
view/progressoverlay.cpp
view/progressoverlay.h
view/searchbar.cpp
view/searchbar.h
view/smartcardswidget.cpp
view/smartcardswidget.h
view/smartcardwidget.cpp
view/smartcardwidget.h
view/tabwidget.cpp
view/tabwidget.h
view/textoverlay.cpp
view/textoverlay.h
view/urllabel.cpp
view/urllabel.h
view/waitwidget.cpp
view/waitwidget.h
view/welcomewidget.cpp
view/welcomewidget.h
aboutdata.cpp
aboutdata.h
kleopatra.qrc
kleopatraapplication.cpp
kleopatraapplication.h
main.cpp
mainwindow.cpp
mainwindow.h
systrayicon.cpp
systrayicon.h
kleopatra_options.h
)
if(WIN32)
configure_file (versioninfo.rc.in versioninfo.rc)
configure_file (kleopatra.w32-manifest.in kleopatra.w32-manifest)
set(_kleopatra_SRCS
${CMAKE_CURRENT_BINARY_DIR}/kleopatra.w32-manifest
${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc
conf/kmessageboxdontaskagainstorage.cpp
conf/kmessageboxdontaskagainstorage.h
${_kleopatra_SRCS}
)
endif()
set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp conf/kleopageconfigdialog.h ${_kleopatra_SRCS})
ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra
DESCRIPTION "kleopatra (kleopatra)"
OLD_CATEGORY_NAMES log_kleopatra
EXPORT KLEOPATRA
)
if(KLEO_MODEL_TEST)
add_definitions(-DKLEO_MODEL_TEST)
set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp)
endif()
ki18n_wrap_ui(_kleopatra_SRCS
dialogs/setinitialpindialog.ui
newcertificatewizard/listwidget.ui
)
kconfig_add_kcfg_files(_kleopatra_SRCS
kcfg/emailoperationspreferences.kcfgc
kcfg/fileoperationspreferences.kcfgc
kcfg/settings.kcfgc
kcfg/smimevalidationpreferences.kcfgc
- kcfg/tagspreferences.kcfgc
kcfg/tooltippreferences.kcfgc
)
file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png")
ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS})
add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS})
# For the ConfigureDialog & KCMs
target_link_libraries(kleopatra_bin kcm_kleopatra_static)
#if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
# set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON)
#endif()
set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra)
if (WIN32)
set(_kleopatra_platform_libs "secur32")
endif ()
target_link_libraries(kleopatra_bin
Gpgmepp
KPim6::Libkleo
KPim6::Mime
KPim6::MimeTreeParserWidgets
KF6::Codecs
KF6::CoreAddons
KF6::Crash
KF6::I18n
KF6::IconThemes
KF6::ItemModels
KF6::KIOCore
KF6::KIOWidgets
KF6::WindowSystem
KF6::XmlGui
Qt::Network
Qt::PrintSupport # Printing secret keys
kleopatraclientcore
${_kleopatra_extra_libs}
${_kleopatra_mail_libs}
${_kleopatra_uiserver_extra_libs}
${_kleopatra_dbusaddons_libs}
${_kleopatra_platform_libs}
)
target_link_libraries(kleopatra_bin QGpgmeQt6)
install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(
PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop
DESTINATION ${KDE_INSTALL_APPDIR}
)
install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES data/kleopatra-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR})
install(
PROGRAMS data/kleopatra_signencryptfiles.desktop
data/kleopatra_signencryptfolders.desktop
data/kleopatra_decryptverifyfiles.desktop
data/kleopatra_decryptverifyfolders.desktop
DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus
)
diff --git a/src/commands/certifycertificatecommand.cpp b/src/commands/certifycertificatecommand.cpp
index 2846776fc..aceafbf3f 100644
--- a/src/commands/certifycertificatecommand.cpp
+++ b/src/commands/certifycertificatecommand.cpp
@@ -1,338 +1,334 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/certifycertificatecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019 g10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certifycertificatecommand.h"
#include "newopenpgpcertificatecommand.h"
#include "command_p.h"
#include "dialogs/certifycertificatedialog.h"
#include "exportopenpgpcertstoservercommand.h"
-#include "utils/tags.h"
#include <Libkleo/Algorithm>
#include <Libkleo/Compat>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <QGpgME/Protocol>
#include <QGpgME/SignKeyJob>
#include <QDate>
#include <QEventLoop>
#include <gpgme++/key.h>
#include "kleopatra_debug.h"
#include <KLocalizedString>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
using namespace QGpgME;
class CertifyCertificateCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::CertifyCertificateCommand;
CertifyCertificateCommand *q_func() const
{
return static_cast<CertifyCertificateCommand *>(q);
}
public:
explicit Private(CertifyCertificateCommand *qq, KeyListController *c);
~Private() override;
void init();
private:
void slotDialogRejected();
void slotResult(const Error &err);
void slotCertificationPrepared();
private:
void ensureDialogCreated();
void createJob();
private:
GpgME::Key target;
std::vector<UserID> uids;
QPointer<CertifyCertificateDialog> dialog;
QPointer<QGpgME::SignKeyJob> job;
};
CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CertifyCertificateCommand::Private::Private(CertifyCertificateCommand *qq, KeyListController *c)
: Command::Private(qq, c)
, uids()
, dialog()
, job()
{
}
CertifyCertificateCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG);
if (dialog) {
delete dialog;
dialog = nullptr;
}
}
CertifyCertificateCommand::CertifyCertificateCommand(KeyListController *c)
: Command(new Private(this, c))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::Key &key)
: Command(key, new Private(this, nullptr))
{
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::UserID &uid)
: Command(uid.parent(), new Private(this, nullptr))
{
std::vector<UserID>(1, uid).swap(d->uids);
d->init();
}
CertifyCertificateCommand::CertifyCertificateCommand(const std::vector<GpgME::UserID> &uids)
: Command(uids.empty() ? Key() : uids.front().parent(), new Private(this, nullptr))
{
d->uids = uids;
d->init();
}
void CertifyCertificateCommand::Private::init()
{
}
CertifyCertificateCommand::~CertifyCertificateCommand()
{
qCDebug(KLEOPATRA_LOG);
}
void CertifyCertificateCommand::doStart()
{
const std::vector<Key> keys = d->keys();
if (keys.size() != 1 || keys.front().protocol() != GpgME::OpenPGP) {
d->finished();
return;
}
// hold on to the key to certify to avoid invalidation during refreshes of the key cache
d->target = keys.front();
if (d->target.isExpired() || d->target.isRevoked()) {
const auto title = d->target.isRevoked() ? i18nc("@title:window", "Key is Revoked") : i18nc("@title:window", "Key is Expired");
const auto message = d->target.isRevoked() //
? i18nc("@info", "This key has been revoked. You cannot certify it.")
: i18nc("@info", "This key has expired. You cannot certify it.");
d->information(message, title);
d->finished();
return;
}
auto findAnyGoodKey = []() {
const std::vector<Key> secKeys = KeyCache::instance()->secretKeys();
return std::any_of(secKeys.cbegin(), secKeys.cend(), [](const Key &secKey) {
return Kleo::keyHasCertify(secKey) && secKey.protocol() == OpenPGP && !secKey.isRevoked() && !secKey.isExpired() && !secKey.isInvalid();
});
};
if (!findAnyGoodKey()) {
auto sel =
KMessageBox::questionTwoActions(d->parentWidgetOrView(),
xi18nc("@info", "To certify other certificates, you first need to create an OpenPGP certificate for yourself.")
+ QStringLiteral("<br><br>") + i18n("Do you wish to create one now?"),
i18nc("@title:window", "Certification Not Possible"),
KGuiItem(i18nc("@action:button", "Create")),
KStandardGuiItem::cancel());
if (sel == KMessageBox::ButtonCode::PrimaryAction) {
QEventLoop loop;
auto cmd = new NewOpenPGPCertificateCommand;
cmd->setParentWidget(d->parentWidgetOrView());
connect(cmd, &Command::finished, &loop, &QEventLoop::quit);
QMetaObject::invokeMethod(cmd, &NewOpenPGPCertificateCommand::start, Qt::QueuedConnection);
loop.exec();
} else {
Q_EMIT(canceled());
d->finished();
return;
}
// Check again for secret keys
if (!findAnyGoodKey()) {
qCDebug(KLEOPATRA_LOG) << "Sec Keys still empty after keygen.";
Q_EMIT(canceled());
d->finished();
return;
}
}
const char *primary = keys.front().primaryFingerprint();
const bool anyMismatch = std::any_of(d->uids.cbegin(), d->uids.cend(), [primary](const UserID &uid) {
return qstricmp(uid.parent().primaryFingerprint(), primary) != 0;
});
if (anyMismatch) {
qCWarning(KLEOPATRA_LOG) << "User ID <-> Key mismatch!";
d->finished();
return;
}
d->ensureDialogCreated();
Q_ASSERT(d->dialog);
d->dialog->setCertificateToCertify(d->target, d->uids);
d->dialog->show();
}
void CertifyCertificateCommand::Private::slotDialogRejected()
{
Q_EMIT q->canceled();
finished();
}
void CertifyCertificateCommand::Private::slotResult(const Error &err)
{
if (err.isCanceled()) {
// do nothing
} else if (err) {
error(i18n("<p>An error occurred while trying to certify<br/><br/>"
"<b>%1</b>:</p><p>\t%2</p>",
Formatting::formatForComboBox(target),
Formatting::errorAsString(err)),
i18n("Certification Error"));
} else if (dialog && dialog->exportableCertificationSelected() && dialog->sendToServer()) {
auto const cmd = new ExportOpenPGPCertsToServerCommand(target);
cmd->start();
} else {
information(i18n("Certification successful."), i18n("Certification Succeeded"));
}
- if (!dialog->tags().isEmpty()) {
- Tags::enableTags();
- }
finished();
}
void CertifyCertificateCommand::Private::slotCertificationPrepared()
{
Q_ASSERT(dialog);
const auto selectedUserIds = dialog->selectedUserIDs();
std::vector<unsigned int> userIdIndexes;
userIdIndexes.reserve(selectedUserIds.size());
for (unsigned int i = 0, numUserIds = target.numUserIDs(); i < numUserIds; ++i) {
const auto userId = target.userID(i);
const bool userIdIsSelected = std::ranges::any_of(selectedUserIds, [userId](const auto &uid) {
return Kleo::userIDsAreEqual(userId, uid);
});
if (userIdIsSelected) {
userIdIndexes.push_back(i);
}
}
createJob();
Q_ASSERT(job);
job->setExportable(dialog->exportableCertificationSelected());
job->setUserIDsToSign(userIdIndexes);
job->setSigningKey(dialog->selectedSecretKey());
if (!dialog->tags().isEmpty()) {
// do not set an empty remark to avoid an empty signature notation (GnuPG bug T5142)
job->setRemark(dialog->tags());
}
job->setDupeOk(true);
if (dialog->trustSignatureSelected() && !dialog->trustSignatureDomain().isEmpty()) {
// always create level 1 trust signatures with complete trust
job->setTrustSignature(TrustSignatureTrust::Complete, 1, dialog->trustSignatureDomain());
}
if (!dialog->expirationDate().isNull()) {
job->setExpirationDate(dialog->expirationDate());
}
if (const Error err = job->start(target)) {
slotResult(err);
}
}
void CertifyCertificateCommand::doCancel()
{
qCDebug(KLEOPATRA_LOG);
if (d->job) {
d->job->slotCancel();
}
}
void CertifyCertificateCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new CertifyCertificateDialog;
applyWindowID(dialog);
connect(dialog, &QDialog::rejected, q, [this]() {
slotDialogRejected();
});
connect(dialog, &QDialog::accepted, q, [this]() {
slotCertificationPrepared();
});
}
void CertifyCertificateCommand::Private::createJob()
{
Q_ASSERT(!job);
Q_ASSERT(target.protocol() == OpenPGP);
const auto backend = QGpgME::openpgp();
if (!backend) {
return;
}
SignKeyJob *const j = backend->signKeyJob();
if (!j) {
return;
}
connect(j, &QGpgME::Job::jobProgress, q, &Command::progress);
connect(j, &SignKeyJob::result, q, [this](const GpgME::Error &result) {
slotResult(result);
});
job = j;
}
#undef d
#undef q
#include "moc_certifycertificatecommand.cpp"
diff --git a/src/commands/certifygroupcommand.cpp b/src/commands/certifygroupcommand.cpp
index e3021f2ea..0b4b71bbc 100644
--- a/src/commands/certifygroupcommand.cpp
+++ b/src/commands/certifygroupcommand.cpp
@@ -1,359 +1,355 @@
/*
commands/certifygroupcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certifygroupcommand.h"
#include "command_p.h"
#include <commands/exportopenpgpcertstoservercommand.h>
#include <dialogs/certifycertificatedialog.h>
-#include <utils/tags.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyHelpers>
#include <QGpgME/Protocol>
#include <QGpgME/SignKeyJob>
#include <QDate>
#include <QProgressDialog>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
namespace
{
struct CertificationResultData {
std::vector<UserID> userIds;
GpgME::Error error;
};
}
class CertifyGroupCommand::Private : public Command::Private
{
friend class ::Kleo::CertifyGroupCommand;
CertifyGroupCommand *q_func() const
{
return static_cast<CertifyGroupCommand *>(q);
}
public:
explicit Private(CertifyGroupCommand *qq);
~Private() override;
void start();
private:
void showDialog();
void certifyCertificates();
void setUpProgressDialog(int numberOfKeysToCertify);
void startNextCertification();
void createJob();
void slotResult(const Error &err);
void wrapUp();
private:
KeyGroup group;
std::vector<Key> certificates;
QPointer<CertifyCertificateDialog> dialog;
QPointer<QProgressDialog> progressDialog;
std::vector<UserID> userIdsToCertify;
struct {
Key certificationKey;
QDate expirationDate;
QString tags;
bool exportable = false;
bool sendToServer = false;
} certificationOptions;
struct {
std::vector<UserID> userIds;
} jobData;
QPointer<QGpgME::SignKeyJob> job;
std::vector<CertificationResultData> results;
};
CertifyGroupCommand::Private *CertifyGroupCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CertifyGroupCommand::Private *CertifyGroupCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CertifyGroupCommand::Private::Private(CertifyGroupCommand *qq)
: Command::Private(qq)
{
}
CertifyGroupCommand::Private::~Private() = default;
void CertifyGroupCommand::Private::start()
{
if (!group.isNull()) {
const auto &groupKeys = group.keys();
certificates = std::vector<GpgME::Key>(groupKeys.begin(), groupKeys.end());
}
if (certificates.empty()) {
finished();
return;
}
if (!allKeysHaveProtocol(certificates, GpgME::OpenPGP)) {
const auto title = i18nc("@title:window", "Group Cannot Be Certified");
const auto message = i18nc("@info", "This group contains S/MIME certificates which cannot be certified.");
information(message, title);
finished();
return;
}
showDialog();
}
void CertifyGroupCommand::Private::showDialog()
{
dialog = new CertifyCertificateDialog;
dialog->setAttribute(Qt::WA_DeleteOnClose);
applyWindowID(dialog);
connect(dialog, &QDialog::accepted, q, [this]() {
certifyCertificates();
});
connect(dialog, &QDialog::rejected, q, [this]() {
canceled();
});
if (!group.isNull()) {
dialog->setGroupName(group.name());
}
dialog->setCertificatesToCertify(certificates);
dialog->show();
}
void CertifyGroupCommand::Private::certifyCertificates()
{
userIdsToCertify = dialog->selectedUserIDs();
if (userIdsToCertify.empty()) {
canceled();
return;
}
certificationOptions.certificationKey = dialog->selectedSecretKey();
certificationOptions.expirationDate = dialog->expirationDate();
certificationOptions.tags = dialog->tags();
certificationOptions.exportable = dialog->exportableCertificationSelected();
certificationOptions.sendToServer = dialog->sendToServer();
setUpProgressDialog(userIdsToCertify.size());
startNextCertification();
}
void CertifyGroupCommand::Private::setUpProgressDialog(int numberOfKeysToCertify)
{
if (progressDialog) {
return;
}
progressDialog = new QProgressDialog{parentWidgetOrView()};
progressDialog->setAttribute(Qt::WA_DeleteOnClose);
progressDialog->setModal(true);
progressDialog->setWindowTitle(i18nc("@title:window", "Certify Certificates"));
progressDialog->setLabelText(i18nc("@info:progress", "Certifying certificates ..."));
progressDialog->setMinimumDuration(1000);
progressDialog->setMaximum(numberOfKeysToCertify);
progressDialog->setValue(0);
connect(progressDialog, &QProgressDialog::canceled, q, &Command::cancel);
connect(q, &Command::finished, progressDialog, [this]() {
progressDialog->accept();
});
}
void CertifyGroupCommand::Private::startNextCertification()
{
Q_ASSERT(!userIdsToCertify.empty());
const auto nextKey = userIdsToCertify.front().parent();
// for now we only deal with primary user IDs
jobData.userIds = {userIdsToCertify.front()};
userIdsToCertify.erase(userIdsToCertify.begin());
const std::vector<unsigned int> userIdIndexes = {0};
createJob();
job->setUserIDsToSign(userIdIndexes);
if (const Error err = job->start(nextKey)) {
QMetaObject::invokeMethod(
q,
[this, err]() {
slotResult(err);
},
Qt::QueuedConnection);
}
}
void CertifyGroupCommand::Private::createJob()
{
Q_ASSERT(!job);
std::unique_ptr<QGpgME::SignKeyJob> newJob{QGpgME::openpgp()->signKeyJob()};
newJob->setDupeOk(true);
newJob->setSigningKey(certificationOptions.certificationKey);
newJob->setExportable(certificationOptions.exportable);
if (!certificationOptions.tags.isEmpty()) {
// do not set an empty remark to avoid an empty signature notation (GnuPG bug T5142)
newJob->setRemark(certificationOptions.tags);
}
if (!certificationOptions.expirationDate.isNull()) {
newJob->setExpirationDate(certificationOptions.expirationDate);
}
connect(newJob.get(), &QGpgME::SignKeyJob::result, q, [this](const GpgME::Error &result) {
slotResult(result);
});
job = newJob.release();
}
void CertifyGroupCommand::Private::slotResult(const Error &err)
{
results.push_back({
jobData.userIds,
err,
});
progressDialog->setValue(results.size());
if (err.isCanceled()) {
finished();
} else if (err && (results.size() == 1) //
&& ((err.sourceID() == GPG_ERR_SOURCE_PINENTRY) //
|| (err.code() == GPG_ERR_BAD_PASSPHRASE))) {
// abort the certification process on certain errors during the first
// certification, e.g. bad passphrase or pinentry timeout, where it's
// better to restart the whole process instead of continuing with the
// next certificate
error(xi18nc("@info",
"<para>The certification of the certificates failed.</para>"
"<para>Error: <message>%1</message></para>",
Formatting::errorAsString(results.front().error)));
finished();
} else if (!userIdsToCertify.empty()) {
job.clear();
jobData.userIds.clear();
startNextCertification();
} else {
wrapUp();
}
}
static QString resultSummary(const std::vector<CertificationResultData> &results)
{
Q_ASSERT(!results.empty());
const int totalCount = results.size();
const int successCount = Kleo::count_if(results, [](const auto &result) {
return !result.error;
});
if (successCount == totalCount) {
return i18nc("@info", "All certificates were certified successfully.");
}
if (successCount == 0) {
// we assume that all attempted certifications failed for the same reason
return xi18nc("@info",
"<para>The certification of all certificates failed.</para>"
"<para>Error: <message>%1</message></para>",
Formatting::errorAsString(results.front().error));
}
return i18ncp("@info", //
"1 of %2 certificates was certified successfully.",
"%1 of %2 certificates were certified successfully.",
successCount,
totalCount);
}
void CertifyGroupCommand::Private::wrapUp()
{
Q_ASSERT(userIdsToCertify.empty());
Q_ASSERT(!results.empty());
const int successCount = Kleo::count_if(results, [](const auto &result) {
return !result.error;
});
const bool sendToServer = (successCount > 0) && certificationOptions.exportable && certificationOptions.sendToServer;
QString message = QLatin1StringView{"<p>"} + resultSummary(results) + QLatin1String{"</p>"};
if (sendToServer) {
message += i18nc("@info", "<p>Next the certified certificates will be uploaded to the configured certificate directory.</p>");
}
const auto failedUserIdsInfo = std::accumulate(results.cbegin(), results.cend(), QStringList{}, [](auto failedUserIds, const auto &result) {
if (result.error) {
failedUserIds.push_back(i18nc("A user ID (an error description)",
"%1 (%2)",
Formatting::formatForComboBox(result.userIds.front().parent()),
Formatting::errorAsString(result.error)));
}
return failedUserIds;
});
if (successCount > 0) {
if (failedUserIdsInfo.size() > 0) {
message += i18nc("@info", "<p>Certifying the following certificates failed:</p>");
}
informationList(message, failedUserIdsInfo, i18nc("@title:window", "Certification Completed"));
} else {
error(message);
}
if (sendToServer) {
const auto certificatesToSendToServer = std::accumulate(results.cbegin(), results.cend(), std::vector<Key>{}, [](auto keys, const auto &result) {
if (!result.error) {
keys.push_back(result.userIds.front().parent());
}
return keys;
});
const auto cmd = new ExportOpenPGPCertsToServerCommand(certificatesToSendToServer);
cmd->start();
}
- if (!certificationOptions.tags.isEmpty()) {
- Tags::enableTags();
- }
finished();
}
CertifyGroupCommand::CertifyGroupCommand(const KeyGroup &group)
: Command{new Private{this}}
{
d->group = group;
}
CertifyGroupCommand::~CertifyGroupCommand() = default;
void CertifyGroupCommand::doStart()
{
d->start();
}
void CertifyGroupCommand::doCancel()
{
if (d->dialog) {
d->dialog->close();
}
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_certifygroupcommand.cpp"
diff --git a/src/conf/CMakeLists.txt b/src/conf/CMakeLists.txt
index 4897c1915..7c9233226 100644
--- a/src/conf/CMakeLists.txt
+++ b/src/conf/CMakeLists.txt
@@ -1,80 +1,79 @@
include_directories(${kleopatra_SOURCE_DIR}/src)
set(_kcm_kleopatra_libkleopatraclient_extra_SRCS
smimevalidationconfigurationwidget.cpp
smimevalidationconfigurationpage.cpp
cryptooperationsconfigwidget.cpp
cryptooperationsconfigpage.cpp
smimevalidationconfigurationwidget.h
smimevalidationconfigurationpage.h
cryptooperationsconfigwidget.h
cryptooperationsconfigpage.h
smartcardconfigpage.cpp
smartcardconfigpage.h
)
ki18n_wrap_ui(_kcm_kleopatra_libkleopatraclient_extra_SRCS
smimevalidationconfigurationwidget.ui
)
kconfig_add_kcfg_files(_kcm_kleopatra_libkleopatraclient_extra_SRCS
${kleopatra_SOURCE_DIR}/src/kcfg/smimevalidationpreferences.kcfgc
)
set(_kcm_kleopatra_libkleopatraclient_extra_LIBS kleopatraclientgui)
set(kcm_kleopatra_PART_SRCS
dirservconfigpage.cpp
appearanceconfigpage.cpp
appearanceconfigwidget.cpp
gnupgsystemconfigurationpage.cpp
dirservconfigpage.h
appearanceconfigpage.h
appearanceconfigwidget.h
gnupgsystemconfigurationpage.h
labelledwidget.cpp labelledwidget.h
labelledwidget.cpp labelledwidget.h
pluralhandlingspinbox.cpp
pluralhandlingspinbox.h
kleoconfigmodule.cpp
kleoconfigmodule.h
${kleopatra_BINARY_DIR}/src/kleopatra_debug.cpp
${_kcm_kleopatra_libkleopatraclient_extra_SRCS}
)
ki18n_wrap_ui(kcm_kleopatra_PART_SRCS
smimevalidationconfigurationwidget.ui
)
kconfig_add_kcfg_files(kcm_kleopatra_PART_SRCS
${kleopatra_SOURCE_DIR}/src/kcfg/tooltippreferences.kcfgc
${kleopatra_SOURCE_DIR}/src/kcfg/emailoperationspreferences.kcfgc
${kleopatra_SOURCE_DIR}/src/kcfg/fileoperationspreferences.kcfgc
- ${kleopatra_SOURCE_DIR}/src/kcfg/tagspreferences.kcfgc
${kleopatra_SOURCE_DIR}/src/kcfg/settings.kcfgc
)
add_library(kcm_kleopatra_static STATIC ${kcm_kleopatra_PART_SRCS})
set_property(TARGET kcm_kleopatra_static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(kcm_kleopatra_static
KPim6::Libkleo
KF6::ConfigGui
KF6::I18n
KF6::IconThemes
KF6::IconWidgets
KF6::WidgetsAddons
${_kcm_kleopatra_extra_libs}
${_kleopatra_dbusaddons_libs}
${_kcm_kleopatra_libkleopatraclient_extra_LIBS}
)
if (NOT WIN32)
add_library(kleopatra_config_gnupgsystem MODULE kcm_gnupgsystemconfigurationpage.cpp)
target_link_libraries(kleopatra_config_gnupgsystem kcm_kleopatra_static KF6::CoreAddons)
install(TARGETS kleopatra_config_gnupgsystem DESTINATION ${KDE_INSTALL_PLUGINDIR}/pim6/kcms/kleopatra)
endif()
diff --git a/src/conf/appearanceconfigwidget.cpp b/src/conf/appearanceconfigwidget.cpp
index 145520586..b50f441d0 100644
--- a/src/conf/appearanceconfigwidget.cpp
+++ b/src/conf/appearanceconfigwidget.cpp
@@ -1,919 +1,898 @@
/*
appearanceconfigwidget.cpp
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2002, 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2002, 2003 Marc Mutz <mutz@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "appearanceconfigwidget.h"
#include "pluralhandlingspinbox.h"
#include <settings.h>
-#include <tagspreferences.h>
#include <tooltippreferences.h>
#include <Libkleo/DNAttributeOrderConfigWidget>
#include <Libkleo/Dn>
#include <Libkleo/ExpiryCheckerConfig>
#include <Libkleo/KeyFilterManager>
#include <Libkleo/SystemInfo>
#include <KConfig>
#include <KConfigGroup>
#include <KIconDialog>
#include <KLocalizedString>
#include <KMessageWidget>
#include <KSeparator>
#include <QApplication>
#include <QCheckBox>
#include <QColor>
#include <QColorDialog>
#include <QFont>
#include <QFontDialog>
#include <QGridLayout>
#include <QIcon>
#include <QLabel>
#include <QListWidget>
#include <QRegularExpression>
#include <QString>
#include <QVBoxLayout>
#include <algorithm>
using namespace Kleo;
using namespace Kleo::Config;
enum {
HasNameRole = Qt::UserRole + 0x1234, /*!< Records that the user has assigned a name (to avoid comparing with i18n-strings) */
HasFontRole, /*!< Records that the user has chosen completely different font (as opposed to italic/bold/strikeout) */
IconNameRole, /*!< Records the name of the icon (since QIcon won't give it out again, once set) */
MayChangeNameRole,
MayChangeForegroundRole,
MayChangeBackgroundRole,
MayChangeFontRole,
MayChangeItalicRole,
MayChangeBoldRole,
MayChangeStrikeOutRole,
MayChangeIconRole,
StoredForegroundRole, /*!< Stores the actual configured foreground color */
StoredBackgroundRole, /*!< Stores the actual configured background color */
EndDummy,
};
static QFont tryToFindFontFor(const QListWidgetItem *item)
{
if (item)
if (const QListWidget *const lw = item->listWidget()) {
return lw->font();
}
return QApplication::font("QListWidget");
}
static bool is(const QListWidgetItem *item, bool (QFont::*func)() const)
{
if (!item) {
return false;
}
const QVariant v = item->data(Qt::FontRole);
if (!v.isValid() || v.userType() != QMetaType::QFont) {
return false;
}
return (v.value<QFont>().*func)();
}
static bool is_italic(const QListWidgetItem *item)
{
return is(item, &QFont::italic);
}
static bool is_bold(const QListWidgetItem *item)
{
return is(item, &QFont::bold);
}
static bool is_strikeout(const QListWidgetItem *item)
{
return is(item, &QFont::strikeOut);
}
static void set(QListWidgetItem *item, bool on, void (QFont::*func)(bool))
{
if (!item) {
return;
}
const QVariant v = item->data(Qt::FontRole);
QFont font = v.isValid() && v.userType() == QMetaType::QFont ? v.value<QFont>() : tryToFindFontFor(item);
(font.*func)(on);
item->setData(Qt::FontRole, font);
}
static void set_italic(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setItalic);
}
static void set_bold(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setBold);
}
static void set_strikeout(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setStrikeOut);
}
static void apply_config(const KConfigGroup &group, QListWidgetItem *item)
{
if (!item) {
return;
}
const QString name = group.readEntry("Name");
item->setText(name.isEmpty() ? i18nc("Key filter without user-assigned name", "<unnamed>") : name);
item->setData(HasNameRole, !name.isEmpty());
item->setData(MayChangeNameRole, !group.isEntryImmutable("Name"));
const QColor fg = group.readEntry("foreground-color", QColor());
item->setData(StoredForegroundRole, fg.isValid() ? QBrush(fg) : QVariant());
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::ForegroundRole, fg.isValid() ? QBrush(fg) : QVariant());
}
item->setData(MayChangeForegroundRole, !group.isEntryImmutable("foreground-color"));
const QColor bg = group.readEntry("background-color", QColor());
item->setData(StoredBackgroundRole, bg.isValid() ? QBrush(bg) : QVariant());
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::BackgroundRole, bg.isValid() ? QBrush(bg) : QVariant());
}
item->setData(MayChangeBackgroundRole, !group.isEntryImmutable("background-color"));
const QFont defaultFont = tryToFindFontFor(item);
if (group.hasKey("font")) {
const QFont font = group.readEntry("font", defaultFont);
item->setData(Qt::FontRole, font != defaultFont ? font : QVariant());
item->setData(HasFontRole, font != defaultFont);
} else {
QFont font = defaultFont;
font.setStrikeOut(group.readEntry("font-strikeout", false));
font.setItalic(group.readEntry("font-italic", false));
font.setBold(group.readEntry("font-bold", false));
item->setData(Qt::FontRole, font);
item->setData(HasFontRole, false);
}
item->setData(MayChangeFontRole, !group.isEntryImmutable("font"));
item->setData(MayChangeItalicRole, !group.isEntryImmutable("font-italic"));
item->setData(MayChangeBoldRole, !group.isEntryImmutable("font-bold"));
item->setData(MayChangeStrikeOutRole, !group.isEntryImmutable("font-strikeout"));
const QString iconName = group.readEntry("icon");
item->setData(Qt::DecorationRole, iconName.isEmpty() ? QVariant() : QIcon::fromTheme(iconName));
item->setData(IconNameRole, iconName.isEmpty() ? QVariant() : iconName);
item->setData(MayChangeIconRole, !group.isEntryImmutable("icon"));
}
static void erase_if_allowed(QListWidgetItem *item, int role, int allowRole)
{
if (item && item->data(allowRole).toBool()) {
item->setData(role, QVariant());
}
}
#if 0
static void erase_if_allowed(QListWidgetItem *item, const int role[], size_t numRoles, int allowRole)
{
if (item && item->data(allowRole).toBool())
for (unsigned int i = 0; i < numRoles; ++i) {
item->setData(role[i], QVariant());
}
}
static void erase_if_allowed(QListWidgetItem *item, int role, const int allowRole[], size_t numAllowRoles)
{
if (!item) {
return;
}
for (unsigned int i = 0; i < numAllowRoles; ++i)
if (!item->data(allowRole[i]).toBool()) {
return;
}
item->setData(role, QVariant());
}
#endif
static void erase_if_allowed(QListWidgetItem *item, const int role[], size_t numRoles, const int allowRole[], size_t numAllowRoles)
{
if (!item) {
return;
}
for (unsigned int i = 0; i < numAllowRoles; ++i)
if (!item->data(allowRole[i]).toBool()) {
return;
}
for (unsigned int i = 0; i < numRoles; ++i) {
item->setData(role[i], QVariant());
}
}
static void set_default_appearance(QListWidgetItem *item)
{
if (!item) {
return;
}
erase_if_allowed(item, StoredForegroundRole, MayChangeForegroundRole);
erase_if_allowed(item, Qt::ForegroundRole, MayChangeForegroundRole);
erase_if_allowed(item, StoredBackgroundRole, MayChangeBackgroundRole);
erase_if_allowed(item, Qt::BackgroundRole, MayChangeBackgroundRole);
erase_if_allowed(item, Qt::DecorationRole, MayChangeIconRole);
static const int fontRoles[] = {Qt::FontRole, HasFontRole};
static const int fontAllowRoles[] = {
MayChangeFontRole,
MayChangeItalicRole,
MayChangeBoldRole,
MayChangeStrikeOutRole,
};
erase_if_allowed(item, fontRoles, sizeof(fontRoles) / sizeof(int), fontAllowRoles, sizeof(fontAllowRoles) / sizeof(int));
}
static void writeOrDelete(KConfigGroup &group, const char *key, const QVariant &value)
{
if (value.isValid()) {
group.writeEntry(key, value);
} else {
group.deleteEntry(key);
}
}
static QVariant brush2color(const QVariant &v)
{
if (v.isValid()) {
if (v.userType() == QMetaType::QColor) {
return v;
} else if (v.userType() == QMetaType::QBrush) {
return v.value<QBrush>().color();
}
}
return QVariant();
}
static void save_to_config(const QListWidgetItem *item, KConfigGroup &group)
{
if (!item) {
return;
}
writeOrDelete(group, "Name", item->data(HasNameRole).toBool() ? item->text() : QVariant());
writeOrDelete(group, "foreground-color", brush2color(item->data(StoredForegroundRole)));
writeOrDelete(group, "background-color", brush2color(item->data(StoredBackgroundRole)));
writeOrDelete(group, "icon", item->data(IconNameRole));
group.deleteEntry("font");
group.deleteEntry("font-strikeout");
group.deleteEntry("font-italic");
group.deleteEntry("font-bold");
if (item->data(HasFontRole).toBool()) {
writeOrDelete(group, "font", item->data(Qt::FontRole));
return;
}
if (is_strikeout(item)) {
group.writeEntry("font-strikeout", true);
}
if (is_italic(item)) {
group.writeEntry("font-italic", true);
}
if (is_bold(item)) {
group.writeEntry("font-bold", true);
}
}
static void kiosk_enable(QWidget *w, const QListWidgetItem *item, int allowRole)
{
if (!w) {
return;
}
if (item && !item->data(allowRole).toBool()) {
w->setEnabled(false);
w->setToolTip(i18nc("@info:tooltip", "This parameter has been locked down by the system administrator."));
} else {
w->setEnabled(item);
w->setToolTip(QString());
}
}
class Ui_AppearanceConfigWidget
{
public:
QTabWidget *tabWidget;
KMessageWidget *highContrastMsg;
QListWidget *categoriesLV;
QPushButton *iconButton;
QPushButton *foregroundButton;
QPushButton *backgroundButton;
QPushButton *fontButton;
QCheckBox *italicCB;
QCheckBox *boldCB;
QCheckBox *strikeoutCB;
QPushButton *defaultLookPB;
QCheckBox *tooltipValidityCheckBox;
QCheckBox *tooltipOwnerCheckBox;
QCheckBox *tooltipDetailsCheckBox;
- QCheckBox *useTagsCheckBox;
QCheckBox *showExpirationCheckBox;
PluralHandlingSpinBox *ownCertificateThresholdSpinBox;
PluralHandlingSpinBox *otherCertificateThresholdSpinBox;
void setupUi(QWidget *parent)
{
if (parent->objectName().isEmpty())
parent->setObjectName(QString::fromUtf8("AppearanceConfigWidget"));
auto mainLayout = new QVBoxLayout{parent};
mainLayout->setContentsMargins({});
tabWidget = new QTabWidget(parent);
tabWidget->setDocumentMode(true);
tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
{
auto tab = new QWidget{parent};
auto tabLayout = new QVBoxLayout{tab};
- useTagsCheckBox = new QCheckBox{i18nc("@option:check", "Show tags attached to certificates"), tab};
- useTagsCheckBox->setToolTip(i18nc("@info:tooltip", "Enable display and usage of tags attached to certificates."));
- tabLayout->addWidget(useTagsCheckBox);
-
tabLayout->addWidget(new KSeparator{tab});
auto label = new QLabel{tab};
label->setText(i18nc("@info", "Show the following information in certificate list tooltips:"));
tabLayout->addWidget(label);
tooltipValidityCheckBox = new QCheckBox{i18nc("@option:check", "Show validity"), tab};
tabLayout->addWidget(tooltipValidityCheckBox);
tooltipOwnerCheckBox = new QCheckBox{i18nc("@option:check", "Show owner information"), tab};
tabLayout->addWidget(tooltipOwnerCheckBox);
tooltipDetailsCheckBox = new QCheckBox{i18nc("@option:check", "Show technical details"), tab};
tabLayout->addWidget(tooltipDetailsCheckBox);
tabLayout->addWidget(new KSeparator{tab});
showExpirationCheckBox = new QCheckBox{i18nc("@option:check", "Show upcoming certificate expiration"), tab};
tabLayout->addWidget(showExpirationCheckBox);
{
auto gridLayout = new QGridLayout;
const ExpiryCheckerConfig expiryConfig;
{
auto label = new QLabel{i18nc("@label:spinbox", "Threshold for own certificates:"), tab};
ownCertificateThresholdSpinBox = new PluralHandlingSpinBox{tab};
label->setBuddy(ownCertificateThresholdSpinBox);
const auto configItem = expiryConfig.ownKeyThresholdInDaysItem();
ownCertificateThresholdSpinBox->setMinimum(configItem->minValue().toInt());
ownCertificateThresholdSpinBox->setMaximum(configItem->maxValue().toInt());
ownCertificateThresholdSpinBox->setSpecialValueText(i18nc("@item never show expiry notification", "never"));
ownCertificateThresholdSpinBox->setSuffix(ki18ncp("@item:valuesuffix", " day", " days"));
ownCertificateThresholdSpinBox->setToolTip(
i18nc("@info:tooltip", "Select the number of days you want to be warned in advance, if your own certificate is about to expire soon."));
gridLayout->addWidget(label, 0, 0);
gridLayout->addWidget(ownCertificateThresholdSpinBox, 0, 1);
}
{
auto label = new QLabel{i18nc("@label:spinbox", "Threshold for other certificates:"), tab};
otherCertificateThresholdSpinBox = new PluralHandlingSpinBox{tab};
label->setBuddy(otherCertificateThresholdSpinBox);
const auto configItem = expiryConfig.otherKeyThresholdInDaysItem();
otherCertificateThresholdSpinBox->setMinimum(configItem->minValue().toInt());
otherCertificateThresholdSpinBox->setMaximum(configItem->maxValue().toInt());
otherCertificateThresholdSpinBox->setSpecialValueText(i18nc("@item never show expiry notification", "never"));
otherCertificateThresholdSpinBox->setSuffix(ki18ncp("@item:valuesuffix", " day", " days"));
otherCertificateThresholdSpinBox->setToolTip(
i18nc("@info:tooltip",
"Select the number of days you want to be warned in advance, if another person's certificate is about to expire soon."));
gridLayout->addWidget(label, 1, 0);
gridLayout->addWidget(otherCertificateThresholdSpinBox, 1, 1);
}
gridLayout->setColumnStretch(2, 1);
tabLayout->addLayout(gridLayout);
}
tabLayout->addStretch(1);
tabWidget->addTab(tab, i18nc("@title:tab", "General"));
}
auto tab_2 = new QWidget();
tab_2->setObjectName(QString::fromUtf8("tab_2"));
auto gridLayout = new QGridLayout(tab_2);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
highContrastMsg = new KMessageWidget(tab_2);
highContrastMsg->setObjectName(QString::fromUtf8("highContrastMsg"));
gridLayout->addWidget(highContrastMsg, 0, 0, 1, 2);
categoriesLV = new QListWidget(tab_2);
categoriesLV->setObjectName(QString::fromUtf8("categoriesLV"));
gridLayout->addWidget(categoriesLV, 1, 0, 1, 1);
auto vboxLayout = new QVBoxLayout();
vboxLayout->setObjectName(QString::fromUtf8("vboxLayout"));
iconButton = new QPushButton(tab_2);
iconButton->setText(i18nc("@action:button", "Set Icon..."));
iconButton->setObjectName(QString::fromUtf8("iconButton"));
iconButton->setEnabled(false);
vboxLayout->addWidget(iconButton);
foregroundButton = new QPushButton(tab_2);
foregroundButton->setText(i18nc("@action:button", "Set Text Color..."));
foregroundButton->setObjectName(QString::fromUtf8("foregroundButton"));
foregroundButton->setEnabled(false);
vboxLayout->addWidget(foregroundButton);
backgroundButton = new QPushButton(tab_2);
backgroundButton->setText(i18nc("@action:button", "Set Background Color..."));
backgroundButton->setObjectName(QString::fromUtf8("backgroundButton"));
backgroundButton->setEnabled(false);
vboxLayout->addWidget(backgroundButton);
fontButton = new QPushButton(tab_2);
fontButton->setText(i18nc("@action:button", "Set Font..."));
fontButton->setObjectName(QString::fromUtf8("fontButton"));
fontButton->setEnabled(false);
vboxLayout->addWidget(fontButton);
italicCB = new QCheckBox(tab_2);
italicCB->setText(i18nc("@option:check", "Italic"));
italicCB->setObjectName(QString::fromUtf8("italicCB"));
italicCB->setEnabled(false);
vboxLayout->addWidget(italicCB);
boldCB = new QCheckBox(tab_2);
boldCB->setText(i18nc("@option:check", "Bold"));
boldCB->setObjectName(QString::fromUtf8("boldCB"));
boldCB->setEnabled(false);
vboxLayout->addWidget(boldCB);
strikeoutCB = new QCheckBox(tab_2);
strikeoutCB->setText(i18nc("@option:check", "Strikeout"));
strikeoutCB->setObjectName(QString::fromUtf8("strikeoutCB"));
strikeoutCB->setEnabled(false);
vboxLayout->addWidget(strikeoutCB);
vboxLayout->addStretch(1);
defaultLookPB = new QPushButton(tab_2);
defaultLookPB->setText(i18nc("@action:button", "Default Appearance"));
defaultLookPB->setObjectName(QString::fromUtf8("defaultLookPB"));
defaultLookPB->setEnabled(false);
vboxLayout->addWidget(defaultLookPB);
gridLayout->addLayout(vboxLayout, 1, 1, 1, 1);
tabWidget->addTab(tab_2, i18nc("@title:tab", "Certificate Categories"));
mainLayout->addWidget(tabWidget);
}
};
class AppearanceConfigWidget::Private : public Ui_AppearanceConfigWidget
{
friend class ::Kleo::Config::AppearanceConfigWidget;
AppearanceConfigWidget *const q;
public:
explicit Private(AppearanceConfigWidget *qq)
: Ui_AppearanceConfigWidget()
, q(qq)
{
setupUi(q);
if (QLayout *const l = q->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
highContrastMsg->setVisible(SystemInfo::isHighContrastModeActive());
highContrastMsg->setMessageType(KMessageWidget::Warning);
highContrastMsg->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, q));
highContrastMsg->setText(i18n("The preview of colors is disabled because high-contrast mode is active."));
highContrastMsg->setCloseButtonVisible(false);
if (Kleo::Settings{}.cmsEnabled()) {
auto w = new QWidget;
dnOrderWidget = new DNAttributeOrderConfigWidget{w};
dnOrderWidget->setObjectName(QLatin1StringView("dnOrderWidget"));
(new QVBoxLayout(w))->addWidget(dnOrderWidget);
tabWidget->addTab(w, i18n("DN-Attribute Order"));
connect(dnOrderWidget, &DNAttributeOrderConfigWidget::changed, q, &AppearanceConfigWidget::changed);
}
connect(iconButton, SIGNAL(clicked()), q, SLOT(slotIconClicked()));
#ifndef QT_NO_COLORDIALOG
connect(foregroundButton, SIGNAL(clicked()), q, SLOT(slotForegroundClicked()));
connect(backgroundButton, SIGNAL(clicked()), q, SLOT(slotBackgroundClicked()));
#else
foregroundButton->hide();
backgroundButton->hide();
#endif
#ifndef QT_NO_FONTDIALOG
connect(fontButton, SIGNAL(clicked()), q, SLOT(slotFontClicked()));
#else
fontButton->hide();
#endif
auto emitChanged = [this]() {
Q_EMIT q->changed();
};
connect(categoriesLV, SIGNAL(itemSelectionChanged()), q, SLOT(slotSelectionChanged()));
connect(defaultLookPB, SIGNAL(clicked()), q, SLOT(slotDefaultClicked()));
connect(italicCB, SIGNAL(toggled(bool)), q, SLOT(slotItalicToggled(bool)));
connect(boldCB, SIGNAL(toggled(bool)), q, SLOT(slotBoldToggled(bool)));
connect(strikeoutCB, SIGNAL(toggled(bool)), q, SLOT(slotStrikeOutToggled(bool)));
connect(tooltipValidityCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipValidityChanged(bool)));
connect(tooltipOwnerCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipOwnerChanged(bool)));
connect(tooltipDetailsCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipDetailsChanged(bool)));
- connect(useTagsCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotUseTagsChanged(bool)));
connect(showExpirationCheckBox, &QCheckBox::toggled, q, emitChanged);
connect(ownCertificateThresholdSpinBox, &QSpinBox::valueChanged, q, emitChanged);
connect(otherCertificateThresholdSpinBox, &QSpinBox::valueChanged, q, emitChanged);
}
private:
void enableDisableActions(QListWidgetItem *item);
QListWidgetItem *selectedItem() const;
private:
void slotIconClicked();
#ifndef QT_NO_COLORDIALOG
void slotForegroundClicked();
void slotBackgroundClicked();
#endif
#ifndef QT_NO_FONTDIALOG
void slotFontClicked();
#endif
void slotSelectionChanged();
void slotDefaultClicked();
void slotItalicToggled(bool);
void slotBoldToggled(bool);
void slotStrikeOutToggled(bool);
void slotTooltipValidityChanged(bool);
void slotTooltipOwnerChanged(bool);
void slotTooltipDetailsChanged(bool);
- void slotUseTagsChanged(bool);
private:
Kleo::DNAttributeOrderConfigWidget *dnOrderWidget = nullptr;
};
AppearanceConfigWidget::AppearanceConfigWidget(QWidget *p, Qt::WindowFlags f)
: QWidget(p, f)
, d(new Private(this))
{
// load();
}
AppearanceConfigWidget::~AppearanceConfigWidget()
{
}
void AppearanceConfigWidget::Private::slotSelectionChanged()
{
enableDisableActions(selectedItem());
}
QListWidgetItem *AppearanceConfigWidget::Private::selectedItem() const
{
const QList<QListWidgetItem *> items = categoriesLV->selectedItems();
return items.empty() ? nullptr : items.front();
}
void AppearanceConfigWidget::Private::enableDisableActions(QListWidgetItem *item)
{
kiosk_enable(iconButton, item, MayChangeIconRole);
#ifndef QT_NO_COLORDIALOG
kiosk_enable(foregroundButton, item, MayChangeForegroundRole);
kiosk_enable(backgroundButton, item, MayChangeBackgroundRole);
#endif
#ifndef QT_NO_FONTDIALOG
kiosk_enable(fontButton, item, MayChangeFontRole);
#endif
kiosk_enable(italicCB, item, MayChangeItalicRole);
kiosk_enable(boldCB, item, MayChangeBoldRole);
kiosk_enable(strikeoutCB, item, MayChangeStrikeOutRole);
defaultLookPB->setEnabled(item);
italicCB->setChecked(is_italic(item));
boldCB->setChecked(is_bold(item));
strikeoutCB->setChecked(is_strikeout(item));
}
void AppearanceConfigWidget::Private::slotDefaultClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
set_default_appearance(item);
enableDisableActions(item);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::defaults()
{
// use temporary KConfigSkeleton instances for (re)setting the values to the defaults;
// the setters respect the immutability of the individual settings, so that we don't have
// to check this ourselves
Settings settings;
settings.setShowExpiryNotifications(settings.findItem(QStringLiteral("ShowExpiryNotifications"))->getDefault().toBool());
d->showExpirationCheckBox->setChecked(settings.showExpiryNotifications());
{
ExpiryCheckerConfig expiryConfig;
expiryConfig.setOwnKeyThresholdInDays(expiryConfig.ownKeyThresholdInDaysItem()->getDefault().toInt());
d->ownCertificateThresholdSpinBox->setValue(expiryConfig.ownKeyThresholdInDays());
expiryConfig.setOtherKeyThresholdInDays(expiryConfig.otherKeyThresholdInDaysItem()->getDefault().toInt());
d->otherCertificateThresholdSpinBox->setValue(expiryConfig.otherKeyThresholdInDays());
}
// This simply means "default look for every category"
for (int i = 0, end = d->categoriesLV->count(); i != end; ++i) {
set_default_appearance(d->categoriesLV->item(i));
}
TooltipPreferences tooltipPrefs;
tooltipPrefs.setShowValidity(tooltipPrefs.findItem(QStringLiteral("ShowValidity"))->getDefault().toBool());
d->tooltipValidityCheckBox->setChecked(tooltipPrefs.showValidity());
tooltipPrefs.setShowOwnerInformation(tooltipPrefs.findItem(QStringLiteral("ShowOwnerInformation"))->getDefault().toBool());
d->tooltipOwnerCheckBox->setChecked(tooltipPrefs.showOwnerInformation());
tooltipPrefs.setShowCertificateDetails(tooltipPrefs.findItem(QStringLiteral("ShowCertificateDetails"))->getDefault().toBool());
d->tooltipDetailsCheckBox->setChecked(tooltipPrefs.showCertificateDetails());
if (d->dnOrderWidget) {
if (!settings.isImmutable(QStringLiteral("AttributeOrder"))) {
d->dnOrderWidget->setAttributeOrder(DN::defaultAttributeOrder());
}
}
Q_EMIT changed();
}
void AppearanceConfigWidget::load()
{
const Settings settings;
d->showExpirationCheckBox->setChecked(settings.showExpiryNotifications());
d->showExpirationCheckBox->setEnabled(!settings.isImmutable(QStringLiteral("ShowExpiryNotifications")));
{
const ExpiryCheckerConfig expiryConfig;
d->ownCertificateThresholdSpinBox->setValue(expiryConfig.ownKeyThresholdInDays());
d->ownCertificateThresholdSpinBox->setEnabled(!expiryConfig.ownKeyThresholdInDaysItem()->isImmutable());
d->otherCertificateThresholdSpinBox->setValue(expiryConfig.otherKeyThresholdInDays());
d->otherCertificateThresholdSpinBox->setEnabled(!expiryConfig.otherKeyThresholdInDaysItem()->isImmutable());
}
if (d->dnOrderWidget) {
d->dnOrderWidget->setAttributeOrder(DN::attributeOrder());
d->dnOrderWidget->setEnabled(!settings.isImmutable(QStringLiteral("AttributeOrder")));
}
d->categoriesLV->clear();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
if (!config) {
return;
}
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
for (const QString &group : groups) {
const KConfigGroup configGroup{config, group};
const bool isCmsSpecificKeyFilter = !configGroup.readEntry("is-openpgp-key", true);
auto item = new QListWidgetItem{d->categoriesLV};
// hide CMS-specific filters if CMS is disabled; we hide those filters
// instead of skipping them, so that they are not removed on save
item->setHidden(isCmsSpecificKeyFilter && !Kleo::Settings{}.cmsEnabled());
apply_config(configGroup, item);
}
const TooltipPreferences prefs;
d->tooltipValidityCheckBox->setChecked(prefs.showValidity());
d->tooltipValidityCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowValidity")));
d->tooltipOwnerCheckBox->setChecked(prefs.showOwnerInformation());
d->tooltipOwnerCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowOwnerInformation")));
d->tooltipDetailsCheckBox->setChecked(prefs.showCertificateDetails());
d->tooltipDetailsCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowCertificateDetails")));
-
- const TagsPreferences tagsPrefs;
- d->useTagsCheckBox->setChecked(tagsPrefs.useTags());
- d->useTagsCheckBox->setEnabled(!tagsPrefs.isImmutable(QStringLiteral("UseTags")));
}
void AppearanceConfigWidget::save()
{
Settings settings;
settings.setShowExpiryNotifications(d->showExpirationCheckBox->isChecked());
if (d->dnOrderWidget) {
settings.setAttributeOrder(d->dnOrderWidget->attributeOrder());
DN::setAttributeOrder(settings.attributeOrder());
}
settings.save();
{
ExpiryCheckerConfig expiryConfig;
expiryConfig.setOwnKeyThresholdInDays(d->ownCertificateThresholdSpinBox->value());
expiryConfig.setOtherKeyThresholdInDays(d->otherCertificateThresholdSpinBox->value());
expiryConfig.save();
}
TooltipPreferences prefs;
prefs.setShowValidity(d->tooltipValidityCheckBox->isChecked());
prefs.setShowOwnerInformation(d->tooltipOwnerCheckBox->isChecked());
prefs.setShowCertificateDetails(d->tooltipDetailsCheckBox->isChecked());
prefs.save();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
if (!config) {
return;
}
// We know (assume) that the groups in the config object haven't changed,
// so we just iterate over them and over the listviewitems, and map one-to-one.
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
#if 0
if (groups.isEmpty()) {
// If we created the default categories ourselves just now, then we need to make up their list
Q3ListViewItemIterator lvit(categoriesLV);
for (; lvit.current(); ++lvit) {
groups << lvit.current()->text(0);
}
}
#endif
for (int i = 0, end = std::min<int>(groups.size(), d->categoriesLV->count()); i != end; ++i) {
const QListWidgetItem *const item = d->categoriesLV->item(i);
Q_ASSERT(item);
KConfigGroup group(config, groups[i]);
save_to_config(item, group);
}
- TagsPreferences tagsPrefs;
- tagsPrefs.setUseTags(d->useTagsCheckBox->isChecked());
- tagsPrefs.save();
-
config->sync();
KeyFilterManager::instance()->reload();
}
void AppearanceConfigWidget::Private::slotIconClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QString iconName = KIconDialog::getIcon(/* repeating default arguments begin */
KIconLoader::Desktop,
KIconLoader::Application,
false,
0,
false,
/* repeating default arguments end */
q);
if (iconName.isEmpty()) {
return;
}
item->setIcon(QIcon::fromTheme(iconName));
item->setData(IconNameRole, iconName);
Q_EMIT q->changed();
}
#ifndef QT_NO_COLORDIALOG
void AppearanceConfigWidget::Private::slotForegroundClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = brush2color(item->data(StoredForegroundRole));
const QColor initial = v.isValid() ? v.value<QColor>() : categoriesLV->palette().color(QPalette::Normal, QPalette::Text);
const QColor c = QColorDialog::getColor(initial, q);
if (c.isValid()) {
item->setData(StoredForegroundRole, QBrush(c));
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::ForegroundRole, QBrush(c));
}
Q_EMIT q->changed();
}
}
void AppearanceConfigWidget::Private::slotBackgroundClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = brush2color(item->data(StoredBackgroundRole));
const QColor initial = v.isValid() ? v.value<QColor>() : categoriesLV->palette().color(QPalette::Normal, QPalette::Base);
const QColor c = QColorDialog::getColor(initial, q);
if (c.isValid()) {
item->setData(StoredBackgroundRole, QBrush(c));
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::BackgroundRole, QBrush(c));
}
Q_EMIT q->changed();
}
}
#endif // QT_NO_COLORDIALOG
#ifndef QT_NO_FONTDIALOG
void AppearanceConfigWidget::Private::slotFontClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = item->data(Qt::FontRole);
bool ok = false;
const QFont defaultFont = tryToFindFontFor(item);
const QFont initial = v.isValid() && v.userType() == QMetaType::QFont ? v.value<QFont>() : defaultFont;
QFont f = QFontDialog::getFont(&ok, initial, q);
if (!ok) {
return;
}
// disallow circumventing KIOSK:
if (!item->data(MayChangeItalicRole).toBool()) {
f.setItalic(initial.italic());
}
if (!item->data(MayChangeBoldRole).toBool()) {
f.setBold(initial.bold());
}
if (!item->data(MayChangeStrikeOutRole).toBool()) {
f.setStrikeOut(initial.strikeOut());
}
item->setData(Qt::FontRole, f != defaultFont ? f : QVariant());
item->setData(HasFontRole, true);
Q_EMIT q->changed();
}
#endif // QT_NO_FONTDIALOG
void AppearanceConfigWidget::Private::slotItalicToggled(bool on)
{
set_italic(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotBoldToggled(bool on)
{
set_bold(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotStrikeOutToggled(bool on)
{
set_strikeout(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipValidityChanged(bool)
{
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipOwnerChanged(bool)
{
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipDetailsChanged(bool)
{
Q_EMIT q->changed();
}
-void AppearanceConfigWidget::Private::slotUseTagsChanged(bool)
-{
- Q_EMIT q->changed();
-}
-
#include "moc_appearanceconfigwidget.cpp"
diff --git a/src/conf/appearanceconfigwidget.h b/src/conf/appearanceconfigwidget.h
index 47723bdd5..1539acdc2 100644
--- a/src/conf/appearanceconfigwidget.h
+++ b/src/conf/appearanceconfigwidget.h
@@ -1,59 +1,58 @@
/*
appearanceconfigwidget.h
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2002, 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2002, 2003 Marc Mutz <mutz@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <memory>
namespace Kleo
{
namespace Config
{
class AppearanceConfigWidget : public QWidget
{
Q_OBJECT
public:
explicit AppearanceConfigWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~AppearanceConfigWidget() override;
void load();
void save();
void defaults();
Q_SIGNALS:
void changed();
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotIconClicked())
#ifndef QT_NO_COLORDIALOG
Q_PRIVATE_SLOT(d, void slotForegroundClicked())
Q_PRIVATE_SLOT(d, void slotBackgroundClicked())
#endif
#ifndef QT_NO_FONTDIALOG
Q_PRIVATE_SLOT(d, void slotFontClicked())
#endif
Q_PRIVATE_SLOT(d, void slotSelectionChanged())
Q_PRIVATE_SLOT(d, void slotDefaultClicked())
Q_PRIVATE_SLOT(d, void slotItalicToggled(bool))
Q_PRIVATE_SLOT(d, void slotBoldToggled(bool))
Q_PRIVATE_SLOT(d, void slotStrikeOutToggled(bool))
Q_PRIVATE_SLOT(d, void slotTooltipValidityChanged(bool))
Q_PRIVATE_SLOT(d, void slotTooltipDetailsChanged(bool))
Q_PRIVATE_SLOT(d, void slotTooltipOwnerChanged(bool))
- Q_PRIVATE_SLOT(d, void slotUseTagsChanged(bool))
};
}
}
diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp
index 405e4f79f..6208200d1 100644
--- a/src/dialogs/certificatedetailswidget.cpp
+++ b/src/dialogs/certificatedetailswidget.cpp
@@ -1,732 +1,731 @@
/*
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 "cardinfotab.h"
#include "certificatedumpwidget.h"
#include "dialogs/weboftrustwidget.h"
#include "kleopatra_debug.h"
#include "revokerswidget.h"
#include "subkeyswidget.h"
#include "trustchainwidget.h"
#include "useridswidget.h"
#include "commands/changeexpirycommand.h"
#include "commands/detailscommand.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.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 <QStackedWidget>
#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;
class CertificateDetailsWidget::Private
{
public:
Private(CertificateDetailsWidget *qq);
void setupCommonProperties();
void setUpSMIMEAdressList();
void setupPGPProperties();
void setupSMIMEProperties();
void refreshCertificate();
void changeExpiration();
void keysMayHaveChanged();
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);
void setTabVisible(QWidget *tab, bool visible);
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 {
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;
std::unique_ptr<InfoField> statusField;
std::unique_ptr<InfoField> usageField;
QListWidget *smimeAddressList = nullptr;
QTabWidget *tabWidget = nullptr;
SubKeysWidget *subKeysWidget = nullptr;
WebOfTrustWidget *webOfTrustWidget = nullptr;
TrustChainWidget *trustChainWidget = nullptr;
CertificateDumpWidget *certificateDumpWidget = nullptr;
CardInfoTab *cardInfoTab = nullptr;
RevokersWidget *revokersWidget = nullptr;
void setupUi(QWidget *parent)
{
auto mainLayout = new QVBoxLayout{parent};
{
auto gridLayout = new QGridLayout;
gridLayout->setColumnStretch(1, 1);
int row = -1;
row++;
primaryUserIdField = std::make_unique<InfoField>(i18n("User ID:"), parent);
gridLayout->addWidget(primaryUserIdField->label(), row, 0);
gridLayout->addLayout(primaryUserIdField->layout(), 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++;
statusField = std::make_unique<InfoField>(i18n("Status:"), parent);
gridLayout->addWidget(statusField->label(), row, 0);
gridLayout->addLayout(statusField->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++;
usageField = std::make_unique<InfoField>(i18n("Usage:"), parent);
gridLayout->addWidget(usageField->label(), row, 0);
gridLayout->addLayout(usageField->layout(), row, 1);
row++;
trustedIntroducerField = std::make_unique<InfoField>(i18n("Trusted introducer for:"), parent);
gridLayout->addWidget(trustedIntroducerField->label(), row, 0);
trustedIntroducerField->setToolTip(i18nc("@info:tooltip", "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);
mainLayout->addLayout(gridLayout);
}
tabWidget = new QTabWidget(parent);
tabWidget->setDocumentMode(true); // we don't want a frame around the page widgets
tabWidget->tabBar()->setDrawBase(false); // only draw the tabs
mainLayout->addWidget(tabWidget);
userIDs = new UserIdsWidget(parent);
tabWidget->addTab(userIDs, i18nc("@title:tab", "User IDs"));
smimeAddressList = new QListWidget{parent};
// Breeze draws no frame for scroll areas that are the only widget in a layout...unless we force it
smimeAddressList->setProperty("_breeze_force_frame", true);
smimeAddressList->setAccessibleName(i18n("Related addresses"));
smimeAddressList->setEditTriggers(QAbstractItemView::NoEditTriggers);
smimeAddressList->setSelectionMode(QAbstractItemView::SingleSelection);
tabWidget->addTab(smimeAddressList, i18nc("@title:tab", "Related Addresses"));
subKeysWidget = new SubKeysWidget(parent);
tabWidget->addTab(subKeysWidget, i18nc("@title:tab", "Subkeys"));
webOfTrustWidget = new WebOfTrustWidget(parent);
tabWidget->addTab(webOfTrustWidget, i18nc("@title:tab", "Certifications"));
trustChainWidget = new TrustChainWidget(parent);
tabWidget->addTab(trustChainWidget, i18nc("@title:tab", "Trust Chain Details"));
cardInfoTab = new CardInfoTab(parent);
tabWidget->addTab(cardInfoTab, i18nc("@title:tab", "Smartcard"));
certificateDumpWidget = new CertificateDumpWidget(parent);
tabWidget->addTab(certificateDumpWidget, i18nc("@title:tab", "Certificate Dump"));
revokersWidget = new RevokersWidget(parent);
#if GPGME_VERSION_NUMBER >= 0x011800 // 1.24.0
const auto index = tabWidget->addTab(revokersWidget, i18nc("@title:tab", "Revokers"));
tabWidget->setTabVisible(index, false);
#endif
}
} ui;
};
CertificateDetailsWidget::Private::Private(CertificateDetailsWidget *qq)
: q{qq}
{
ui.setupUi(q);
connect(ui.changeExpirationAction, &QAction::triggered, q, [this]() {
changeExpiration();
});
connect(ui.showIssuerCertificateAction, &QAction::triggered, q, [this]() {
showIssuerCertificate();
});
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();
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()
// update availability of buttons
ui.changeExpirationAction->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()));
ui.statusField->setValue(Formatting::complianceStringShort(key));
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("As in 'this secret key is stored on smart card <serial number>'", "smart card %1", QString::fromUtf8(serialNo));
} else {
storage = i18nc("As in 'this secret key is stored on a smart card'", "smart card");
}
} else if (!subkey.isSecret()) {
storage = i18nc("key is 'offline key', i.e. secret key is not stored on this computer", "offline");
} else if (KeyCache::instance()->cardsForSubkey(subkey).size() > 0) {
storage = i18ncp("As in 'this key is stored on this computer and on smart card(s)'",
"On this computer and on a smart card",
"On this computer and on %1 smart cards",
KeyCache::instance()->cardsForSubkey(subkey).size());
} else {
storage = i18nc("As in 'this secret key is stored on this computer'", "on this computer");
}
ui.privateKeyInfoField->setValue(storage);
if (DeVSCompliance::isCompliant()) {
ui.complianceField->setValue(Kleo::Formatting::complianceStringForKey(key));
}
ui.usageField->setVisible(key.protocol() == GpgME::CMS);
QStringList usage;
if (subkey.canEncrypt()) {
usage += i18n("encryption");
}
if (subkey.canSign()) {
usage += i18n("signing");
}
if (subkey.canCertify()) {
usage += i18n("certification");
}
if (subkey.canAuthenticate()) {
usage += i18n("authentication");
}
if (subkey.isQualified()) {
usage += i18nc("as in: can be used for ...", "qualified signatures");
}
ui.usageField->setValue(usage.join(i18nc("Separator between words in a list", ", ")));
ui.cardInfoTab->setKey(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.tabWidget->setTabVisible(1, false);
}
}
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();
}
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);
}
}
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::setTabVisible(QWidget *tab, bool visible)
{
ui.tabWidget->setTabVisible(ui.tabWidget->indexOf(tab), visible);
}
void CertificateDetailsWidget::Private::setupPGPProperties()
{
setTabVisible(ui.userIDs, true);
setTabVisible(ui.smimeAddressList, false);
setTabVisible(ui.subKeysWidget, true);
setTabVisible(ui.webOfTrustWidget, true);
setTabVisible(ui.trustChainWidget, false);
setTabVisible(ui.certificateDumpWidget, false);
ui.userIDs->setKey(key);
ui.subKeysWidget->setKey(key);
ui.webOfTrustWidget->setKey(key);
ui.revokersWidget->setKey(key);
#if GPGME_VERSION_NUMBER >= 0x011800 // 1.24.0
setTabVisible(ui.revokersWidget, key.numRevocationKeys() > 0);
#endif
const auto trustDomains = accumulateTrustDomains(key.userIDs());
ui.trustedIntroducerField->setVisible(!trustDomains.empty());
ui.trustedIntroducerField->setValue(QStringList(std::begin(trustDomains), std::end(trustDomains)).join(u", "));
ui.primaryUserIdField->setValue(Formatting::prettyUserID(key.userID(0)));
ui.primaryUserIdField->setVisible(true);
}
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()
{
setTabVisible(ui.userIDs, false);
setTabVisible(ui.smimeAddressList, true);
setTabVisible(ui.subKeysWidget, false);
setTabVisible(ui.webOfTrustWidget, false);
setTabVisible(ui.trustChainWidget, true);
setTabVisible(ui.certificateDumpWidget, true);
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());
ui.primaryUserIdField->setVisible(false);
ui.certificateDumpWidget->setKey(key);
setUpSMIMEAdressList();
}
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 &)
{
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->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);
ctx->addKeyListMode(GpgME::WithKeygrip);
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/useridswidget.cpp b/src/dialogs/useridswidget.cpp
index c83c62c80..9b7e8ac4d 100644
--- a/src/dialogs/useridswidget.cpp
+++ b/src/dialogs/useridswidget.cpp
@@ -1,526 +1,525 @@
// 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;
}
static QPushButton *addActionButton(QLayout *buttonBox, QAction *action)
{
if (!action) {
return nullptr;
}
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);
return button;
}
}
class UserIdsWidget::Private
{
public:
Private(UserIdsWidget *qq)
: q{qq}
{
}
TreeWidget *userIDTable = nullptr;
QPushButton *addUserIDBtn = nullptr;
QPushButton *revokeUserIDBtn = nullptr;
QPushButton *certifyBtn = nullptr;
QPushButton *revokeCertificationsBtn = nullptr;
QAction *setPrimaryUserIDAction = nullptr;
QAction *certifyAction = nullptr;
QAction *revokeCertificationsAction = nullptr;
GpgME::Key key;
bool updateInProgress = false;
QPushButton *moreButton = nullptr;
QHBoxLayout *buttonRow = nullptr;
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({});
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);
d->buttonRow = new QHBoxLayout;
d->addUserIDBtn = new QPushButton(i18nc("@action:button", "Add User ID"), parent);
d->buttonRow->addWidget(d->addUserIDBtn);
d->revokeUserIDBtn = new QPushButton(i18nc("@action:button", "Revoke User ID"), parent);
d->buttonRow->addWidget(d->revokeUserIDBtn);
d->setPrimaryUserIDAction = new QAction({}, i18nc("@action:button", "Flag as Primary"));
d->setPrimaryUserIDAction->setToolTip(i18nc("@info:tooltip", "Flag the selected user ID as the primary user ID of this key."));
d->certifyAction = new QAction({}, i18nc("@action:button", "Certify User IDs"));
d->revokeCertificationsAction = new QAction({}, i18nc("@action:button", "Revoke Certifications"));
d->certifyBtn = addActionButton(d->buttonRow, d->certifyAction);
d->revokeCertificationsBtn = addActionButton(d->buttonRow, d->revokeCertificationsAction);
d->moreButton = new QPushButton(QIcon::fromTheme(QStringLiteral("application-menu")), {});
d->moreButton->setToolTip(i18nc("@info:tooltip", "Show more options"));
d->buttonRow->addWidget(d->moreButton);
connect(d->moreButton, &QPushButton::clicked, this, [this]() {
auto menu = new QMenu(this);
menu->addAction(d->setPrimaryUserIDAction);
menu->addAction(d->certifyAction);
menu->addAction(d->revokeCertificationsAction);
menu->popup(d->moreButton->mapToGlobal(QPoint()));
});
d->buttonRow->addStretch(1);
userIDsLayout->addLayout(d->buttonRow);
setLayout(userIDsLayout);
connect(d->addUserIDBtn, &QPushButton::clicked, this, [this]() {
d->addUserID();
});
connect(d->userIDTable, &QTreeWidget::itemSelectionChanged, this, [this]() {
d->updateUserIDActions();
});
connect(d->setPrimaryUserIDAction, &QAction::triggered, this, [this]() {
d->setPrimaryUserID();
});
connect(d->certifyAction, &QAction::triggered, this, [this]() {
d->certifyUserIDs();
});
connect(d->revokeCertificationsAction, &QAction::triggered, 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));
setPrimaryUserIDAction->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->setPrimaryUserIDAction->setVisible(isOwnKey);
d->setPrimaryUserIDAction->setEnabled(false); // requires a selected user ID
d->certifyAction->setVisible(true); // always visible (for OpenPGP keys)
d->certifyBtn->setVisible(!isOwnKey);
d->revokeCertificationsBtn->setVisible(!isOwnKey);
d->moreButton->setVisible(isOwnKey);
d->certifyAction->setEnabled(isLocalKey && keyCanBeCertified && userCanSignUserIDs);
d->revokeCertificationsAction->setVisible(Kleo::Commands::RevokeCertificationCommand::isSupported());
d->revokeCertificationsAction->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"), i18n("Origin")};
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);
item->setData(4, Qt::DisplayRole, Formatting::origin(uid.origin()));
userIDTable->addTopLevelItem(item);
}
- userIDTable->restoreColumnLayout(QStringLiteral("UserIDTable"));
- if (!Tags::tagsEnabled()) {
+ if (!userIDTable->restoreColumnLayout(QStringLiteral("UserIDTable"))) {
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);
setPrimaryUserIDAction->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();
certifyAction->setEnabled(true);
});
certifyAction->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();
revokeCertificationsAction->setEnabled(true);
});
revokeCertificationsAction->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/weboftrustwidget.cpp b/src/dialogs/weboftrustwidget.cpp
index 03b37818c..896179d19 100644
--- a/src/dialogs/weboftrustwidget.cpp
+++ b/src/dialogs/weboftrustwidget.cpp
@@ -1,440 +1,434 @@
/*
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 <Libkleo/UserIDListProxyModel>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
#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;
UserIDListProxyModel proxyModel;
QGpgME::KeyListJob *keyListJob = nullptr;
TreeView *certificationsTV = nullptr;
QAction *detailsAction = nullptr;
QAction *certifyAction = nullptr;
QAction *revokeAction = nullptr;
QAction *fetchAction = nullptr;
QLabel *notAvailableLabel = nullptr;
QPushButton *moreButton = nullptr;
QCheckBox *showOnlyOwnCheck = nullptr;
public:
Private(WebOfTrustWidget *qq)
: q{qq}
{
- certificationsModel.enableRemarks(Tags::tagsEnabled());
+ certificationsModel.enableRemarks(true);
auto vLay = new QVBoxLayout(q);
vLay->setContentsMargins({});
proxyModel.setSourceModel(&certificationsModel);
certificationsTV = new TreeView{q};
certificationsTV->setAccessibleName(i18n("User IDs and certifications"));
certificationsTV->setModel(&proxyModel);
certificationsTV->setAllColumnsShowFocus(false);
certificationsTV->setSelectionMode(QAbstractItemView::SingleSelection);
- if (!Tags::tagsEnabled()) {
- certificationsTV->hideColumn(static_cast<int>(UserIDListModel::Column::Tags));
- }
vLay->addWidget(certificationsTV);
notAvailableLabel = new QLabel(i18nc("@info", "Certifications are not available before the certificate is imported."));
notAvailableLabel->setAlignment(Qt::AlignHCenter);
notAvailableLabel->setVisible(false);
vLay->addWidget(notAvailableLabel);
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();
});
}
fetchAction = new QAction(QIcon::fromTheme(QStringLiteral("download")), i18nc("@action:button", "Fetch Missing Keys"));
fetchAction->setToolTip(i18nc("@info:tooltip", "Look up and import all keys that were used to certify the user IDs of this key"));
connect(fetchAction, &QAction::triggered, q, [this]() {
fetchMissingKeys();
});
auto bbox = new QHBoxLayout;
addActionButton(bbox, certifyAction);
addActionButton(bbox, revokeAction);
moreButton = new QPushButton(QIcon::fromTheme(QStringLiteral("application-menu")), {});
moreButton->setToolTip(i18nc("@info:tooltip", "Show more options"));
bbox->addWidget(moreButton);
connect(moreButton, &QPushButton::clicked, q, [this]() {
auto menu = new QMenu(q);
menu->addAction(detailsAction);
menu->addAction(fetchAction);
menu->popup(moreButton->mapToGlobal(QPoint()));
});
showOnlyOwnCheck = new QCheckBox(i18nc("label:checkbox", "Show only my own certifications"));
bbox->addWidget(showOnlyOwnCheck);
connect(showOnlyOwnCheck, &QCheckBox::toggled, &proxyModel, &UserIDListProxyModel::setShowOnlyOwnCertifications);
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 proxyModel.userID(certificationsTV->currentIndex());
}
GpgME::UserID::Signature selectedCertification()
{
return proxyModel.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->setParentWidget(q->window());
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(i18nc("@info:tooltip", "Revocation of self-certifications is currently not possible."));
break;
case CertificationIsRevocation:
revokeAction->setToolTip(
i18nc("@info:tooltip", "You cannot revoke this revocation certification. (But you can re-certify the corresponding user ID.)"));
break;
case CertificationIsExpired:
revokeAction->setToolTip(i18nc("@info:tooltip", "You cannot revoke this expired certification."));
break;
case CertificationIsInvalid:
revokeAction->setToolTip(i18nc("@info:tooltip", "You cannot revoke this invalid certification."));
break;
case CertificationKeyNotAvailable:
revokeAction->setToolTip(i18nc("@info:tooltip", "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 userID = proxyModel.userID(certificationsTV->indexAt(p));
const auto signature = proxyModel.signature(certificationsTV->indexAt(p));
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);
- }
+ 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);
fetchAction->setEnabled(false);
connect(cmd, &Kleo::ImportCertificateFromKeyserverCommand::finished, q, [this]() {
// Trigger an update when done
q->setKey(q->key());
fetchAction->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;
}
if (isRemoteKey(key)) {
d->certificationsTV->setVisible(false);
d->notAvailableLabel->setVisible(true);
d->moreButton->setEnabled(false);
}
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->fetchAction->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"
diff --git a/src/kcfg/tagspreferences.kcfg b/src/kcfg/tagspreferences.kcfg
deleted file mode 100644
index 019970832..000000000
--- a/src/kcfg/tagspreferences.kcfg
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
- http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
- <kcfgfile name="kleopatrarc" />
- <group name="RemarkSettings">
- <entry name="UseTags" key="RemarksEnabled" type="Bool">
- <label>Use tags</label>
- <whatsthis>Enable display and usage of tags attached to keys.</whatsthis>
- <default>false</default>
- </entry>
- <entry name="TagKey" key="RemarkKeyFpr" type="String">
- <label>Fingerprint of tag key</label>
- <whatsthis>If a key is specified, then only tags made with this key are considered. Otherwise, tags made with any fully trusted key are considered.</whatsthis>
- <default></default>
- </entry>
- </group>
-</kcfg>
diff --git a/src/kcfg/tagspreferences.kcfgc b/src/kcfg/tagspreferences.kcfgc
deleted file mode 100644
index f9f3f3f2c..000000000
--- a/src/kcfg/tagspreferences.kcfgc
+++ /dev/null
@@ -1,7 +0,0 @@
-# Code generation options for kconfig_compiler
-File=tagspreferences.kcfg
-ClassName=TagsPreferences
-NameSpace=Kleo
-Singleton=false
-Mutators=true
-MemberVariables=private
diff --git a/src/utils/tags.cpp b/src/utils/tags.cpp
index 634d02df5..f02d12ed5 100644
--- a/src/utils/tags.cpp
+++ b/src/utils/tags.cpp
@@ -1,63 +1,28 @@
/* utils/tags.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2019 g10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "tags.h"
-#include "tagspreferences.h"
-
-#include "kleopatra_debug.h"
-
#include <KSharedConfig>
#include <Libkleo/KeyCache>
using namespace Kleo;
-bool Tags::tagsEnabled()
-{
- return TagsPreferences().useTags();
-}
-
-void Tags::enableTags()
-{
- TagsPreferences().setUseTags(true);
- KeyCache::mutableInstance()->enableRemarks(true);
-}
-
-GpgME::Key Tags::tagKey()
-{
- const auto tagKeyFpr = TagsPreferences().tagKey();
- GpgME::Key key;
- if (tagKeyFpr.isEmpty()) {
- return key;
- }
- key = KeyCache::instance()->findByKeyIDOrFingerprint(tagKeyFpr.toLatin1().constData());
- if (key.isNull()) {
- qCDebug(KLEOPATRA_LOG) << "Failed to find tag key: " << tagKeyFpr;
- return key;
- }
- return key;
-}
-
std::vector<GpgME::Key> Tags::tagKeys()
{
std::vector<GpgME::Key> ret;
for (const auto &key : KeyCache::instance()->keys()) {
if (key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid() || key.protocol() != GpgME::OpenPGP) {
continue;
}
if (key.ownerTrust() >= GpgME::Key::Full) {
ret.push_back(key);
}
}
return ret;
}
-
-void Tags::setTagKey(const GpgME::Key &key)
-{
- TagsPreferences().setTagKey(key.isNull() ? QString() : QString::fromLatin1(key.primaryFingerprint()));
-}
diff --git a/src/utils/tags.h b/src/utils/tags.h
index f0d48b4b9..45be30a8d 100644
--- a/src/utils/tags.h
+++ b/src/utils/tags.h
@@ -1,29 +1,22 @@
/* utils/tags.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2019 g10code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <gpgme++/key.h>
#include <vector>
namespace Kleo
{
namespace Tags
{
-/* Helper functions to work with tags configuration */
-bool tagsEnabled();
-void enableTags();
-/* Read / write a single tag key into configuration. */
-GpgME::Key tagKey();
-void setTagKey(const GpgME::Key &key);
-
/* Get multiple keys to use for tags. Currently
* this returns all fully trusted OpenPGP Keys. */
std::vector<GpgME::Key> tagKeys();
}
} // namespace Kleo
diff --git a/src/view/keytreeview.cpp b/src/view/keytreeview.cpp
index b5bc9e215..b1373bef9 100644
--- a/src/view/keytreeview.cpp
+++ b/src/view/keytreeview.cpp
@@ -1,698 +1,690 @@
/* -*- mode: c++; c-basic-offset:4 -*-
view/keytreeview.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keytreeview.h"
#include "searchbar.h"
#include <Libkleo/KeyList>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/KeyRearrangeColumnsProxyModel>
#include <Libkleo/Predicates>
#include <Libkleo/TreeView>
#include "utils/headerview.h"
#include "utils/tags.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilter>
#include <Libkleo/Stl_Util>
#include <gpgme++/key.h>
#include "kleopatra_debug.h"
#include <QAction>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QEvent>
#include <QGuiApplication>
#include <QHeaderView>
#include <QItemSelection>
#include <QItemSelectionModel>
#include <QLayout>
#include <QList>
#include <QMenu>
#include <QTimer>
#include <KLocalizedString>
#include <KSharedConfig>
#include <KStandardAction>
#include <qnamespace.h>
static int tagsColumn;
using namespace Kleo;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
namespace
{
class TreeViewInternal : public Kleo::TreeView
{
public:
explicit TreeViewInternal(QWidget *parent = nullptr)
: Kleo::TreeView{parent}
{
- connect(this, &TreeView::columnEnabled, this, [this](int column) {
- if (column == tagsColumn) {
- Tags::enableTags();
- }
- });
}
QSize minimumSizeHint() const override
{
const QSize min = QTreeView::minimumSizeHint();
return QSize(min.width(), min.height() + 5 * fontMetrics().height());
}
protected:
void focusInEvent(QFocusEvent *event) override
{
QTreeView::focusInEvent(event);
// queue the invokation, so that it happens after the widget itself got focus
QMetaObject::invokeMethod(this, &TreeViewInternal::forceAccessibleFocusEventForCurrentItem, Qt::QueuedConnection);
}
private:
void forceAccessibleFocusEventForCurrentItem()
{
// force Qt to send a focus event for the current item to accessibility
// tools; otherwise, the user has no idea which item is selected when the
// list gets keyboard input focus
const auto current = currentIndex();
setCurrentIndex({});
setCurrentIndex(current);
}
private:
QMenu *mHeaderPopup = nullptr;
QList<QAction *> mColumnActions;
};
const KeyListModelInterface *keyListModel(const QTreeView &view)
{
const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(view.model());
Q_ASSERT(klmi);
return klmi;
}
} // anon namespace
KeyTreeView::KeyTreeView(QWidget *parent)
: QWidget(parent)
, m_proxy(new KeyListSortFilterProxyModel(this))
, m_additionalProxy(nullptr)
, m_view(new TreeViewInternal(this))
, m_flatModel(nullptr)
, m_hierarchicalModel(nullptr)
, m_stringFilter()
, m_keyFilter()
, m_isHierarchical(true)
, m_showDefaultContextMenu(true)
{
init();
}
KeyTreeView::KeyTreeView(const KeyTreeView &other)
: QWidget(nullptr)
, m_proxy(new KeyListSortFilterProxyModel(this))
, m_additionalProxy(other.m_additionalProxy ? other.m_additionalProxy->clone() : nullptr)
, m_view(new TreeViewInternal(this))
, m_flatModel(other.m_flatModel)
, m_hierarchicalModel(other.m_hierarchicalModel)
, m_stringFilter(other.m_stringFilter)
, m_keyFilter(other.m_keyFilter)
, m_group(other.m_group)
, m_isHierarchical(other.m_isHierarchical)
, m_showDefaultContextMenu(other.m_showDefaultContextMenu)
{
init();
setColumnSizes(other.columnSizes());
setSortColumn(other.sortColumn(), other.sortOrder());
}
KeyTreeView::KeyTreeView(const QString &text,
const std::shared_ptr<KeyFilter> &kf,
AbstractKeyListSortFilterProxyModel *proxy,
QWidget *parent,
const KConfigGroup &group,
Options options)
: QWidget(parent)
, m_proxy(new KeyListSortFilterProxyModel(this))
, m_additionalProxy(proxy)
, m_view(new TreeViewInternal(this))
, m_flatModel(nullptr)
, m_hierarchicalModel(nullptr)
, m_stringFilter(text)
, m_keyFilter(kf)
, m_group(group)
, m_isHierarchical(true)
, m_onceResized(false)
, m_showDefaultContextMenu(!(options & Option::NoDefaultContextMenu))
{
init();
}
void KeyTreeView::setColumnSizes(const std::vector<int> &sizes)
{
if (sizes.empty()) {
return;
}
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header()));
if (auto const hv = static_cast<HeaderView *>(m_view->header())) {
hv->setSectionSizes(sizes);
}
}
void KeyTreeView::setSortColumn(int sortColumn, Qt::SortOrder sortOrder)
{
Q_ASSERT(m_view);
m_view->sortByColumn(sortColumn, sortOrder);
}
int KeyTreeView::sortColumn() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
return m_view->header()->sortIndicatorSection();
}
Qt::SortOrder KeyTreeView::sortOrder() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
return m_view->header()->sortIndicatorOrder();
}
std::vector<int> KeyTreeView::columnSizes() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header()));
if (auto const hv = static_cast<HeaderView *>(m_view->header())) {
return hv->sectionSizes();
} else {
return std::vector<int>();
}
}
void KeyTreeView::restoreLayout(const KConfigGroup &group)
{
if (!group.isValid() || !m_view->restoreColumnLayout(group.name())) {
// if config is empty then use default settings
// The numbers have to be in line with the order in
// setsSourceColumns above
m_view->hideColumn(5);
for (int i = 7; i < m_view->model()->columnCount(); ++i) {
m_view->hideColumn(i);
}
if (KeyCache::instance()->initialized()) {
QTimer::singleShot(0, this, &KeyTreeView::initializeColumnSizes);
}
} else {
m_onceResized = true;
}
- if (!m_view->isColumnHidden(tagsColumn)) {
- Tags::enableTags();
- }
}
void KeyTreeView::init()
{
Q_SET_OBJECT_NAME(m_proxy);
Q_SET_OBJECT_NAME(m_view);
if (m_group.isValid()) {
// Reopen as non const
KConfig *conf = m_group.config();
m_group = conf->group(m_group.name());
}
if (m_additionalProxy && m_additionalProxy->objectName().isEmpty()) {
Q_SET_OBJECT_NAME(m_additionalProxy);
}
QLayout *layout = new QVBoxLayout(this);
Q_SET_OBJECT_NAME(layout);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_view);
auto headerView = new HeaderView(Qt::Horizontal);
Q_SET_OBJECT_NAME(headerView);
headerView->installEventFilter(m_view);
headerView->setSectionsMovable(true);
m_view->setHeader(headerView);
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_view->setAllColumnsShowFocus(false);
m_view->setSortingEnabled(true);
m_view->setAccessibleName(i18n("Certificates"));
m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates"));
// we show details on double-click
m_view->setExpandsOnDoubleClick(false);
if (m_showDefaultContextMenu) {
m_view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_view, &KeyTreeView::customContextMenuRequested, this, [this](const auto &pos) {
auto menu = new QMenu;
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(KStandardAction::copy(
this,
[this]() {
QGuiApplication::clipboard()->setText(m_view->currentIndex().data(KeyList::ClipboardRole).toString());
},
this));
menu->popup(m_view->mapToGlobal(pos));
});
}
if (model()) {
if (m_additionalProxy) {
m_additionalProxy->setSourceModel(model());
} else {
m_proxy->setSourceModel(model());
}
}
if (m_additionalProxy) {
m_proxy->setSourceModel(m_additionalProxy);
if (!m_additionalProxy->parent()) {
m_additionalProxy->setParent(this);
}
}
m_proxy->setFilterRegularExpression(QRegularExpression::escape(m_stringFilter.trimmed()));
m_proxy->setKeyFilter(m_keyFilter);
m_proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
auto rearangingModel = new KeyRearrangeColumnsProxyModel(this);
rearangingModel->setSourceModel(m_proxy);
QList<int> columns = {
KeyList::PrettyName,
KeyList::PrettyEMail,
KeyList::Validity,
KeyList::ValidFrom,
KeyList::ValidUntil,
KeyList::TechnicalDetails,
KeyList::KeyID,
KeyList::Fingerprint,
KeyList::OwnerTrust,
KeyList::Origin,
KeyList::LastUpdate,
KeyList::Issuer,
KeyList::SerialNumber,
KeyList::Remarks,
KeyList::Algorithm,
KeyList::Keygrip,
};
tagsColumn = columns.indexOf(KeyList::Remarks);
rearangingModel->setSourceColumns(columns);
m_view->setModel(rearangingModel);
/* Handle expansion state */
if (m_group.isValid()) {
m_expandedKeys = m_group.readEntry("Expanded", QStringList());
}
connect(m_view, &QTreeView::expanded, this, [this](const QModelIndex &index) {
if (!index.isValid()) {
return;
}
const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>();
if (key.isNull()) {
return;
}
const auto fpr = QString::fromLatin1(key.primaryFingerprint());
if (m_expandedKeys.contains(fpr)) {
return;
}
m_expandedKeys << fpr;
if (m_group.isValid()) {
m_group.writeEntry("Expanded", m_expandedKeys);
}
});
connect(m_view, &QTreeView::collapsed, this, [this](const QModelIndex &index) {
if (!index.isValid()) {
return;
}
const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>();
if (key.isNull()) {
return;
}
m_expandedKeys.removeAll(QString::fromLatin1(key.primaryFingerprint()));
if (m_group.isValid()) {
m_group.writeEntry("Expanded", m_expandedKeys);
}
});
updateModelConnections(nullptr, model());
}
void KeyTreeView::restoreExpandState()
{
if (!KeyCache::instance()->initialized()) {
qCWarning(KLEOPATRA_LOG) << "Restore expand state before keycache available. Aborting.";
return;
}
for (const auto &fpr : std::as_const(m_expandedKeys)) {
const KeyListModelInterface *const km = keyListModel(*m_view);
if (!km) {
qCWarning(KLEOPATRA_LOG) << "invalid model";
return;
}
const auto key = KeyCache::instance()->findByFingerprint(fpr.toLatin1().constData());
if (key.isNull()) {
qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in cache";
m_expandedKeys.removeAll(fpr);
return;
}
const auto idx = km->index(key);
if (!idx.isValid()) {
qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in model";
m_expandedKeys.removeAll(fpr);
return;
}
m_view->expand(idx);
}
}
void KeyTreeView::setUpTagKeys()
{
const auto tagKeys = Tags::tagKeys();
if (m_hierarchicalModel) {
m_hierarchicalModel->setRemarkKeys(tagKeys);
}
if (m_flatModel) {
m_flatModel->setRemarkKeys(tagKeys);
}
}
KeyTreeView::~KeyTreeView() = default;
static QAbstractProxyModel *find_last_proxy(QAbstractProxyModel *pm)
{
Q_ASSERT(pm);
while (auto const sm = qobject_cast<QAbstractProxyModel *>(pm->sourceModel())) {
pm = sm;
}
return pm;
}
void KeyTreeView::updateModelConnections(AbstractKeyListModel *oldModel, AbstractKeyListModel *newModel)
{
if (oldModel == newModel) {
return;
}
if (oldModel) {
disconnect(oldModel, &QAbstractItemModel::modelAboutToBeReset, this, &KeyTreeView::saveStateBeforeModelChange);
disconnect(oldModel, &QAbstractItemModel::modelReset, this, &KeyTreeView::restoreStateAfterModelChange);
disconnect(oldModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &KeyTreeView::saveStateBeforeModelChange);
disconnect(oldModel, &QAbstractItemModel::rowsInserted, this, &KeyTreeView::restoreStateAfterModelChange);
disconnect(oldModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &KeyTreeView::saveStateBeforeModelChange);
disconnect(oldModel, &QAbstractItemModel::rowsRemoved, this, &KeyTreeView::restoreStateAfterModelChange);
}
if (newModel) {
connect(newModel, &QAbstractItemModel::modelAboutToBeReset, this, &KeyTreeView::saveStateBeforeModelChange);
connect(newModel, &QAbstractItemModel::modelReset, this, &KeyTreeView::restoreStateAfterModelChange);
connect(newModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &KeyTreeView::saveStateBeforeModelChange);
connect(newModel, &QAbstractItemModel::rowsInserted, this, &KeyTreeView::restoreStateAfterModelChange);
connect(newModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &KeyTreeView::saveStateBeforeModelChange);
connect(newModel, &QAbstractItemModel::rowsRemoved, this, &KeyTreeView::restoreStateAfterModelChange);
}
}
void KeyTreeView::setFlatModel(AbstractKeyListModel *model)
{
if (model == m_flatModel) {
return;
}
auto oldModel = m_flatModel;
m_flatModel = model;
if (!m_isHierarchical)
// TODO: this fails when called after setHierarchicalView( false )...
{
find_last_proxy(m_proxy)->setSourceModel(model);
updateModelConnections(oldModel, model);
}
}
void KeyTreeView::setHierarchicalModel(AbstractKeyListModel *model)
{
if (model == m_hierarchicalModel) {
return;
}
auto oldModel = m_hierarchicalModel;
m_hierarchicalModel = model;
if (m_isHierarchical) {
find_last_proxy(m_proxy)->setSourceModel(model);
updateModelConnections(oldModel, model);
m_view->expandAll();
for (int column = 0; column < m_view->header()->count(); ++column) {
m_view->header()->resizeSection(column, qMax(m_view->header()->sectionSize(column), m_view->header()->sectionSizeHint(column)));
}
}
}
void KeyTreeView::setStringFilter(const QString &filter)
{
if (filter == m_stringFilter) {
return;
}
m_stringFilter = filter;
m_proxy->setFilterRegularExpression(QRegularExpression::escape(filter.trimmed()));
Q_EMIT stringFilterChanged(filter);
}
void KeyTreeView::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
if (filter == m_keyFilter || (filter && m_keyFilter && filter->id() == m_keyFilter->id())) {
return;
}
m_keyFilter = filter;
m_proxy->setKeyFilter(filter);
Q_EMIT keyFilterChanged(filter);
}
namespace
{
QItemSelection itemSelectionFromKeys(const std::vector<Key> &keys, const QTreeView &view)
{
const QModelIndexList indexes = keyListModel(view)->indexes(keys);
return std::accumulate(indexes.cbegin(), indexes.cend(), QItemSelection(), [](QItemSelection selection, const QModelIndex &index) {
if (index.isValid()) {
selection.merge(QItemSelection(index, index), QItemSelectionModel::Select);
}
return selection;
});
}
}
void KeyTreeView::selectKeys(const std::vector<Key> &keys)
{
m_view->selectionModel()->select(itemSelectionFromKeys(keys, *m_view), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
std::vector<Key> KeyTreeView::selectedKeys() const
{
return keyListModel(*m_view)->keys(m_view->selectionModel()->selectedRows());
}
void KeyTreeView::setHierarchicalView(bool on)
{
if (on == m_isHierarchical) {
return;
}
if (on && !hierarchicalModel()) {
qCWarning(KLEOPATRA_LOG) << "hierarchical view requested, but no hierarchical model set";
return;
}
if (!on && !flatModel()) {
qCWarning(KLEOPATRA_LOG) << "flat view requested, but no flat model set";
return;
}
const std::vector<Key> selectedKeys = this->selectedKeys();
const Key currentKey = keyListModel(*m_view)->key(m_view->currentIndex());
auto oldModel = model();
m_isHierarchical = on;
find_last_proxy(m_proxy)->setSourceModel(model());
updateModelConnections(oldModel, model());
if (on) {
m_view->expandAll();
}
selectKeys(selectedKeys);
if (!currentKey.isNull()) {
const QModelIndex currentIndex = keyListModel(*m_view)->index(currentKey);
if (currentIndex.isValid()) {
m_view->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
m_view->scrollTo(currentIndex);
}
}
m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates"));
Q_EMIT hierarchicalChanged(on);
}
void KeyTreeView::setKeys(const std::vector<Key> &keys, const std::vector<Key::Origin> &extraOrigins)
{
std::vector<Key> sorted = keys;
if (extraOrigins.empty()) {
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
}
m_keys = sorted;
if (m_flatModel) {
m_flatModel->setKeys(sorted, extraOrigins);
}
if (m_hierarchicalModel) {
m_hierarchicalModel->setKeys(sorted, extraOrigins);
}
}
void KeyTreeView::addKeysImpl(const std::vector<Key> &keys, bool select)
{
if (keys.empty()) {
return;
}
if (m_keys.empty()) {
setKeys(keys);
return;
}
std::vector<Key> sorted = keys;
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
std::vector<Key> newKeys = _detail::union_by_fpr(sorted, m_keys);
m_keys.swap(newKeys);
if (m_flatModel) {
m_flatModel->addKeys(sorted);
}
if (m_hierarchicalModel) {
m_hierarchicalModel->addKeys(sorted);
}
if (select) {
selectKeys(sorted);
}
}
void KeyTreeView::addKeysSelected(const std::vector<Key> &keys)
{
addKeysImpl(keys, true);
}
void KeyTreeView::addKeysUnselected(const std::vector<Key> &keys)
{
addKeysImpl(keys, false);
}
void KeyTreeView::removeKeys(const std::vector<Key> &keys)
{
if (keys.empty()) {
return;
}
std::vector<Key> sorted = keys;
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
std::vector<Key> newKeys;
newKeys.reserve(m_keys.size());
std::set_difference(m_keys.begin(), m_keys.end(), sorted.begin(), sorted.end(), std::back_inserter(newKeys), _detail::ByFingerprint<std::less>());
m_keys.swap(newKeys);
if (m_flatModel) {
std::for_each(sorted.cbegin(), sorted.cend(), [this](const Key &key) {
m_flatModel->removeKey(key);
});
}
if (m_hierarchicalModel) {
std::for_each(sorted.cbegin(), sorted.cend(), [this](const Key &key) {
m_hierarchicalModel->removeKey(key);
});
}
}
void KeyTreeView::disconnectSearchBar()
{
for (const auto &connection : m_connections) {
disconnect(connection);
}
m_connections.clear();
}
bool KeyTreeView::connectSearchBar(const SearchBar *bar)
{
m_connections.reserve(4);
m_connections.push_back(connect(this, &KeyTreeView::stringFilterChanged, bar, &SearchBar::setStringFilter));
m_connections.push_back(connect(bar, &SearchBar::stringFilterChanged, this, &KeyTreeView::setStringFilter));
m_connections.push_back(connect(this, &KeyTreeView::keyFilterChanged, bar, &SearchBar::setKeyFilter));
m_connections.push_back(connect(bar, &SearchBar::keyFilterChanged, this, &KeyTreeView::setKeyFilter));
return std::all_of(m_connections.cbegin(), m_connections.cend(), [](const QMetaObject::Connection &conn) {
return conn;
});
}
void KeyTreeView::initializeColumnSizes()
{
if (m_onceResized || m_view->model()->rowCount() == 0) {
return;
}
m_onceResized = true;
m_view->setColumnWidth(KeyList::PrettyName, 260);
m_view->setColumnWidth(KeyList::PrettyEMail, 260);
for (int i = 2; i < m_view->model()->columnCount(); ++i) {
m_view->resizeColumnToContents(i);
}
}
void KeyTreeView::saveStateBeforeModelChange()
{
m_currentKey = keyListModel(*m_view)->key(m_view->currentIndex());
m_selectedKeys = selectedKeys();
}
void KeyTreeView::restoreStateAfterModelChange()
{
restoreExpandState();
selectKeys(m_selectedKeys);
if (!m_currentKey.isNull()) {
const QModelIndex currentIndex = keyListModel(*m_view)->index(m_currentKey);
if (currentIndex.isValid()) {
m_view->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
m_view->scrollTo(currentIndex);
}
}
setUpTagKeys();
initializeColumnSizes();
}
void KeyTreeView::keyPressEvent(QKeyEvent *event)
{
if (event == QKeySequence::Copy) {
QGuiApplication::clipboard()->setText(view()->currentIndex().data(KeyList::ClipboardRole).toString());
event->accept();
}
}
#include "moc_keytreeview.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:44 PM (14 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4b/1a/ee1666927c9ad439d61ce6d652a6

Event Timeline