Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index 6f2b43842..597c257e1 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -1,39 +1,31 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
include(ECMAddTests)
find_package(Qt6Test ${REQUIRED_QT_VERSION} CONFIG QUIET)
if(NOT TARGET Qt::Test)
message(STATUS "Qt6Test not found, autotests will not be built.")
return()
endif()
include_directories(${CMAKE_SOURCE_DIR}/src)
ecm_qt_declare_logging_category(logging_category_srcs HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra)
ecm_add_test(
kuniqueservicetest.cpp
${CMAKE_SOURCE_DIR}/src/utils/kuniqueservice.cpp
${logging_category_srcs}
TEST_NAME kuniqueservicetest
LINK_LIBRARIES ${_kleopatra_dbusaddons_libs} Qt::Test
)
ecm_add_test(
stripsuffixtest.cpp
${CMAKE_SOURCE_DIR}/src/utils/path-helper.cpp
${logging_category_srcs}
TEST_NAME stripsuffixtest
LINK_LIBRARIES KF6::I18n KPim6::Libkleo Qt::Test
)
-
-ecm_add_test(
- keyparameterstest.cpp
- ${CMAKE_SOURCE_DIR}/src/utils/keyparameters.cpp
- ${logging_category_srcs}
- TEST_NAME keyparameterstest
- LINK_LIBRARIES KPim6::Libkleo Gpgmepp Qt::Test
-)
diff --git a/autotests/keyparameterstest.cpp b/autotests/keyparameterstest.cpp
deleted file mode 100644
index 73bf662ac..000000000
--- a/autotests/keyparameterstest.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- autotests/keyparameterstest.cpp
-
- This file is part of Kleopatra's test suite.
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include "utils/keyparameters.h"
-
-#include <Libkleo/KeyUsage>
-
-#include <QDate>
-#include <QTest>
-
-#include <gpgme++/key.h>
-
-using namespace Kleo;
-
-class KeyParametersTest : public QObject
-{
- Q_OBJECT
-
-private Q_SLOTS:
- void test_OpenPGP_key()
- {
- KeyParameters params{KeyParameters::OpenPGP};
- params.setName(QStringLiteral("Ada Lovelace"));
- params.setEmail(QStringLiteral("ada.lovelace@example.net"));
- params.setKeyType(GpgME::Subkey::AlgoEDDSA);
- params.setKeyCurve(QStringLiteral("ed25519"));
- params.setKeyUsage(KeyUsage{KeyUsage::Sign | KeyUsage::Authenticate});
- params.setSubkeyType(GpgME::Subkey::AlgoECDH);
- params.setSubkeyCurve(QStringLiteral("cv25519"));
- params.setSubkeyUsage(KeyUsage{KeyUsage::Encrypt});
- params.setExpirationDate(QDate{2024, 12, 10});
-
- QCOMPARE(params.toString(),
- QStringLiteral("<GnupgKeyParms format=\"internal\">\n"
- "%ask-passphrase\n"
- "Key-Type:EdDSA\n"
- "Key-Curve:ed25519\n"
- "Key-Usage:sign auth\n"
- "Subkey-Type:ECDH\n"
- "Subkey-Usage:encrypt\n"
- "Subkey-Curve:cv25519\n"
- "Expire-Date:2024-12-10\n"
- "Name-Real:Ada Lovelace\n"
- "Name-Email:ada.lovelace@example.net\n"
- "</GnupgKeyParms>"));
- }
-
- void test_SMIME_CSR()
- {
- KeyParameters params{KeyParameters::CMS};
- params.setDN(QStringLiteral("CN=Ada Lovelace,L=London,C=UK"));
- params.setEmail(QStringLiteral("ada.lovelace@example.net"));
- params.addEmail(QStringLiteral(u"ada@t\u00E4st.example.org"));
- params.setKeyType(GpgME::Subkey::AlgoRSA);
- params.setKeyLength(3072);
- params.setKeyUsage(KeyUsage{KeyUsage::Sign | KeyUsage::Encrypt});
- params.addDomainName(QStringLiteral("ada.example.net"));
- params.addDomainName(QStringLiteral(u"t\u00E4st.example.org"));
- params.addURI(QStringLiteral("https://ada.example.net"));
- params.addURI(QStringLiteral("https://lovelace.example.org"));
- QCOMPARE(params.toString(),
- QStringLiteral("<GnupgKeyParms format=\"internal\">\n"
- "Key-Type:RSA\n"
- "Key-Length:3072\n"
- "Key-Usage:sign encrypt\n"
- "Name-DN:CN=Ada Lovelace,L=London,C=UK\n"
- "Name-Email:ada.lovelace@example.net\n"
- "Name-Email:ada@xn--tst-qla.example.org\n"
- "Name-DNS:ada.example.net\n"
- "Name-DNS:xn--tst-qla.example.org\n"
- "Name-URI:https://ada.example.net\n"
- "Name-URI:https://lovelace.example.org\n"
- "</GnupgKeyParms>"));
- }
-
- void test_SMIME_CSR_for_card_key()
- {
- KeyParameters params{KeyParameters::CMS};
- params.setDN(QStringLiteral("CN=Ada Lovelace,L=London,C=UK"));
- params.setEmail(QStringLiteral("ada@example.net"));
- params.setCardKeyRef(QStringLiteral("OPENPGP.1"));
- params.setKeyUsage(KeyUsage{KeyUsage::Sign});
- QCOMPARE(params.toString(),
- QStringLiteral("<GnupgKeyParms format=\"internal\">\n"
- "Key-Type:card:OPENPGP.1\n"
- "Key-Usage:sign\n"
- "Name-DN:CN=Ada Lovelace,L=London,C=UK\n"
- "Name-Email:ada@example.net\n"
- "</GnupgKeyParms>"));
- }
-};
-
-QTEST_MAIN(KeyParametersTest)
-#include "keyparameterstest.moc"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3eb5137d2..5fb4d5088 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,658 +1,640 @@
# 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/nameandemailwidget.cpp
- dialogs/nameandemailwidget.h
- dialogs/newopenpgpcertificatedetailsdialog.cpp
- dialogs/newopenpgpcertificatedetailsdialog.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/keyalgo.cpp
- newcertificatewizard/keyalgo_p.h
newcertificatewizard/keycreationpage.cpp
newcertificatewizard/keycreationpage_p.h
newcertificatewizard/listwidget.cpp
newcertificatewizard/listwidget.h
newcertificatewizard/newcertificatewizard.cpp
newcertificatewizard/newcertificatewizard.h
newcertificatewizard/resultpage.cpp
newcertificatewizard/resultpage_p.h
newcertificatewizard/wizardpage.cpp
newcertificatewizard/wizardpage_p.h
selftest/compliancecheck.cpp
selftest/compliancecheck.h
selftest/enginecheck.cpp
selftest/enginecheck.h
selftest/gpgagentcheck.cpp
selftest/gpgagentcheck.h
selftest/gpgconfcheck.cpp
selftest/gpgconfcheck.h
selftest/libkleopatrarccheck.cpp
selftest/libkleopatrarccheck.h
selftest/selftest.cpp
selftest/selftest.h
smartcard/algorithminfo.h
smartcard/card.cpp
smartcard/card.h
smartcard/deviceinfowatcher.cpp
smartcard/deviceinfowatcher.h
smartcard/keypairinfo.cpp
smartcard/keypairinfo.h
smartcard/netkeycard.cpp
smartcard/netkeycard.h
smartcard/openpgpcard.cpp
smartcard/openpgpcard.h
smartcard/p15card.cpp
smartcard/p15card.h
smartcard/pivcard.cpp
smartcard/pivcard.h
smartcard/readerstatus.cpp
smartcard/readerstatus.h
smartcard/utils.cpp
smartcard/utils.h
utils/accessibility.cpp
utils/accessibility.h
utils/action_data.cpp
utils/action_data.h
utils/applicationstate.cpp
utils/applicationstate.h
utils/archivedefinition.cpp
utils/archivedefinition.h
utils/certificatepair.h
utils/clipboardmenu.cpp
utils/clipboardmenu.h
utils/debug-helpers.cpp
utils/debug-helpers.h
utils/dragqueen.cpp
utils/dragqueen.h
utils/email.cpp
utils/email.h
utils/emptypassphraseprovider.cpp
utils/emptypassphraseprovider.h
- utils/expiration.cpp
- utils/expiration.h
utils/filedialog.cpp
utils/filedialog.h
utils/gui-helper.cpp
utils/gui-helper.h
utils/headerview.cpp
utils/headerview.h
utils/input.cpp
utils/input.h
utils/iodevicelogger.cpp
utils/iodevicelogger.h
utils/kdpipeiodevice.cpp
utils/kdpipeiodevice.h
utils/keyexportdraghandler.cpp
utils/keyexportdraghandler.h
- utils/keyparameters.cpp
- utils/keyparameters.h
utils/kuniqueservice.cpp
utils/kuniqueservice.h
utils/log.cpp
utils/log.h
utils/memory-helpers.h
utils/migration.cpp
utils/migration.h
- utils/multivalidator.cpp
- utils/multivalidator.h
utils/output.cpp
utils/output.h
utils/overwritedialog.cpp
utils/overwritedialog.h
utils/path-helper.cpp
utils/path-helper.h
utils/scrollarea.cpp
utils/scrollarea.h
utils/systemtrayicon.cpp
utils/systemtrayicon.h
utils/tags.cpp
utils/tags.h
utils/types.cpp
utils/types.h
utils/userinfo.cpp
utils/userinfo.h
- utils/validation.cpp
- utils/validation.h
utils/writecertassuantransaction.cpp
utils/writecertassuantransaction.h
utils/wsastarter.cpp
utils/wsastarter.h
view/anchorcache.cpp
view/anchorcache_p.h
view/cardkeysview.cpp
view/cardkeysview.h
- view/errorlabel.cpp
- view/errorlabel.h
- view/formtextinput.cpp
- view/formtextinput.h
view/htmllabel.cpp
view/htmllabel.h
view/infofield.cpp
view/infofield.h
view/keycacheoverlay.cpp
view/keycacheoverlay.h
view/keylistcontroller.cpp
view/keylistcontroller.h
view/keytreeview.cpp
view/keytreeview.h
view/netkeywidget.cpp
view/netkeywidget.h
view/nullpinwidget.cpp
view/nullpinwidget.h
view/openpgpkeycardwidget.cpp
view/openpgpkeycardwidget.h
view/overlaywidget.cpp
view/overlaywidget.h
view/p15cardwidget.cpp
view/p15cardwidget.h
view/padwidget.cpp
view/padwidget.h
view/pgpcardwidget.cpp
view/pgpcardwidget.h
view/pivcardwidget.cpp
view/pivcardwidget.h
view/progressoverlay.cpp
view/progressoverlay.h
view/searchbar.cpp
view/searchbar.h
view/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/changeexpirycommand.cpp b/src/commands/changeexpirycommand.cpp
index 2d9c92797..1739419ab 100644
--- a/src/commands/changeexpirycommand.cpp
+++ b/src/commands/changeexpirycommand.cpp
@@ -1,325 +1,325 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/changeexpirycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "changeexpirycommand.h"
#include "command_p.h"
#include "dialogs/expirydialog.h"
-#include "utils/expiration.h"
+#include <Libkleo/Expiration>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <QGpgME/ChangeExpiryJob>
#include <QGpgME/Protocol>
#include <QDateTime>
#include <gpgme++/key.h>
#include <algorithm>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace GpgME;
using namespace QGpgME;
namespace
{
bool subkeyHasSameExpirationAsPrimaryKey(const Subkey &subkey)
{
// we allow for a difference in expiration of up to 1 hour (= 3600 seconds)
static const auto maxExpirationDifference = 3600;
Q_ASSERT(!subkey.isNull());
const auto key = subkey.parent();
const auto primaryKey = key.subkey(0);
const auto primaryExpiration = quint32(primaryKey.expirationTime());
const auto subkeyExpiration = quint32(subkey.expirationTime());
if (primaryExpiration != 0 && subkeyExpiration != 0) {
return (primaryExpiration == subkeyExpiration) //
|| ((primaryExpiration > subkeyExpiration) && (primaryExpiration - subkeyExpiration <= maxExpirationDifference)) //
|| ((primaryExpiration < subkeyExpiration) && (subkeyExpiration - primaryExpiration <= maxExpirationDifference));
}
return primaryKey.neverExpires() && subkey.neverExpires();
}
bool allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(const Key &key)
{
Q_ASSERT(!key.isNull() && key.numSubkeys() > 0);
const auto subkeys = key.subkeys();
return std::all_of(std::begin(subkeys), std::end(subkeys), [](const auto &subkey) {
// revoked subkeys are ignored by gpg --quick-set-expire when updating the expiration of all subkeys;
// check if expiration of subkey is (more or less) the same as the expiration of the primary key
return subkey.isRevoked() || subkeyHasSameExpirationAsPrimaryKey(subkey);
});
}
}
class ChangeExpiryCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::ChangeExpiryCommand;
ChangeExpiryCommand *q_func() const
{
return static_cast<ChangeExpiryCommand *>(q);
}
public:
explicit Private(ChangeExpiryCommand *qq, KeyListController *c);
~Private() override;
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const Error &err);
private:
void ensureDialogCreated(ExpiryDialog::Mode mode);
void createJob();
void showErrorDialog(const Error &error);
void showSuccessDialog();
private:
GpgME::Key key;
GpgME::Subkey subkey;
QPointer<ExpiryDialog> dialog;
QPointer<ChangeExpiryJob> job;
};
ChangeExpiryCommand::Private *ChangeExpiryCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ChangeExpiryCommand::Private *ChangeExpiryCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ChangeExpiryCommand::Private::Private(ChangeExpiryCommand *qq, KeyListController *c)
: Command::Private{qq, c}
{
}
ChangeExpiryCommand::Private::~Private() = default;
void ChangeExpiryCommand::Private::slotDialogAccepted()
{
Q_ASSERT(dialog);
static const QTime END_OF_DAY{23, 59, 00};
const QDateTime expiry{dialog->dateOfExpiry(), END_OF_DAY};
qCDebug(KLEOPATRA_LOG) << "expiry" << expiry;
createJob();
Q_ASSERT(job);
std::vector<Subkey> subkeysToUpdate;
if (!subkey.isNull()) {
// change expiration of a single subkey
if (subkey.keyID() != key.keyID()) { // ignore the primary subkey
subkeysToUpdate.push_back(subkey);
}
} else {
// change expiration of the (primary) key and, optionally, of some subkeys
job->setOptions(ChangeExpiryJob::UpdatePrimaryKey);
if (dialog->updateExpirationOfAllSubkeys() && key.numSubkeys() > 1) {
// explicitly list the subkeys for which the expiration should be changed
// together with the expiration of the (primary) key, so that already expired
// subkeys are also updated
const auto subkeys = key.subkeys();
std::copy_if(std::next(subkeys.begin()), subkeys.end(), std::back_inserter(subkeysToUpdate), [](const auto &subkey) {
// skip revoked subkeys which would anyway be ignored by gpg;
// also skip subkeys without explicit expiration because they inherit the primary key's expiration;
// include all subkeys that are not yet expired or that expired around the same time as the primary key
return !subkey.isRevoked() //
&& !subkey.neverExpires() //
&& (!subkey.isExpired() || subkeyHasSameExpirationAsPrimaryKey(subkey));
});
}
}
if (const Error err = job->start(key, expiry, subkeysToUpdate)) {
showErrorDialog(err);
finished();
}
}
void ChangeExpiryCommand::Private::slotDialogRejected()
{
Q_EMIT q->canceled();
finished();
}
void ChangeExpiryCommand::Private::slotResult(const Error &err)
{
if (err.isCanceled())
;
else if (err) {
showErrorDialog(err);
} else {
showSuccessDialog();
}
finished();
}
void ChangeExpiryCommand::Private::ensureDialogCreated(ExpiryDialog::Mode mode)
{
if (dialog) {
return;
}
dialog = new ExpiryDialog{mode};
applyWindowID(dialog);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, q, [this]() {
slotDialogAccepted();
});
connect(dialog, &QDialog::rejected, q, [this]() {
slotDialogRejected();
});
}
void ChangeExpiryCommand::Private::createJob()
{
Q_ASSERT(!job);
const auto backend = (key.protocol() == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
if (!backend) {
return;
}
ChangeExpiryJob *const j = backend->changeExpiryJob();
if (!j) {
return;
}
connect(j, &QGpgME::Job::jobProgress, q, &Command::progress);
connect(j, &ChangeExpiryJob::result, q, [this](const auto &err) {
slotResult(err);
});
job = j;
}
void ChangeExpiryCommand::Private::showErrorDialog(const Error &err)
{
error(
i18n("<p>An error occurred while trying to change "
"the end of the validity period for <b>%1</b>:</p><p>%2</p>",
Formatting::formatForComboBox(key),
Formatting::errorAsString(err)));
}
void ChangeExpiryCommand::Private::showSuccessDialog()
{
success(i18n("End of validity period changed successfully."));
}
ChangeExpiryCommand::ChangeExpiryCommand(KeyListController *c)
: Command{new Private{this, c}}
{
}
ChangeExpiryCommand::ChangeExpiryCommand(QAbstractItemView *v, KeyListController *c)
: Command{v, new Private{this, c}}
{
}
ChangeExpiryCommand::ChangeExpiryCommand(const GpgME::Key &key)
: Command{key, new Private{this, nullptr}}
{
}
ChangeExpiryCommand::~ChangeExpiryCommand() = default;
void ChangeExpiryCommand::setSubkey(const GpgME::Subkey &subkey)
{
d->subkey = subkey;
}
void ChangeExpiryCommand::doStart()
{
const std::vector<Key> keys = d->keys();
if (keys.size() != 1 //
|| keys.front().protocol() != GpgME::OpenPGP //
|| !keys.front().hasSecret() //
|| keys.front().subkey(0).isNull()) {
d->finished();
return;
}
d->key = keys.front();
if (!d->subkey.isNull() && d->subkey.parent().primaryFingerprint() != d->key.primaryFingerprint()) {
qDebug() << "Invalid subkey" << d->subkey.fingerprint() << ": Not a subkey of key" << d->key.primaryFingerprint();
d->finished();
return;
}
ExpiryDialog::Mode mode;
if (!d->subkey.isNull()) {
mode = ExpiryDialog::Mode::UpdateIndividualSubkey;
} else {
// count the number of (real) subkeys for which the user has the choice to update
// the expiration together with the primary key
const auto subkeys = d->key.subkeys();
const auto numSubkeys = std::count_if(std::next(subkeys.begin()), subkeys.end(), [](const auto &subkey) {
// skip revoked subkeys which would anyway be ignored by gpg;
// also skip subkeys without explicit expiration because they inherit the primary key's expiration;
// include all subkeys that are not yet expired or that expired around the same time as the primary key
return !subkey.isRevoked() //
&& !subkey.neverExpires() //
&& (!subkey.isExpired() || subkeyHasSameExpirationAsPrimaryKey(subkey));
});
if (numSubkeys == 0) {
mode = ExpiryDialog::Mode::UpdateCertificateWithoutSubkeys;
} else {
mode = ExpiryDialog::Mode::UpdateCertificateWithSubkeys;
}
}
d->ensureDialogCreated(mode);
Q_ASSERT(d->dialog);
const Subkey subkey = !d->subkey.isNull() ? d->subkey : d->key.subkey(0);
d->dialog->setDateOfExpiry((subkey.neverExpires() //
? QDate{} //
- : defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration)));
+ : Kleo::Expiration::defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration)));
if (mode == ExpiryDialog::Mode::UpdateIndividualSubkey && subkey.keyID() != subkey.parent().keyID()) {
d->dialog->setPrimaryKey(subkey.parent());
} else if (mode == ExpiryDialog::Mode::UpdateCertificateWithSubkeys) {
d->dialog->setUpdateExpirationOfAllSubkeys(allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(d->key));
}
d->dialog->show();
}
void ChangeExpiryCommand::doCancel()
{
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_changeexpirycommand.cpp"
diff --git a/src/commands/createcsrforcardkeycommand.cpp b/src/commands/createcsrforcardkeycommand.cpp
index 025ab29e7..19e2716a5 100644
--- a/src/commands/createcsrforcardkeycommand.cpp
+++ b/src/commands/createcsrforcardkeycommand.cpp
@@ -1,307 +1,307 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/createcsrforcardkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "createcsrforcardkeycommand.h"
#include "cardcommand_p.h"
#include "dialogs/createcsrforcardkeydialog.h"
#include "smartcard/netkeycard.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "utils/filedialog.h"
-#include "utils/keyparameters.h"
#include <Libkleo/Formatting>
+#include <Libkleo/KeyParameters>
#include <Libkleo/KeyUsage>
#include <KLocalizedString>
#include <KLocalizedString>
#include <QDateTime>
#include <QFile>
#include <QUrl>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
#include <gpgme++/context.h>
#include <gpgme++/engineinfo.h>
#include <gpgme++/keygenerationresult.h>
#include <gpgme.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace Kleo::SmartCard;
using namespace GpgME;
using namespace QGpgME;
class CreateCSRForCardKeyCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::CreateCSRForCardKeyCommand;
CreateCSRForCardKeyCommand *q_func() const
{
return static_cast<CreateCSRForCardKeyCommand *>(q);
}
public:
explicit Private(CreateCSRForCardKeyCommand *qq, const std::string &keyRef, const std::string &serialNumber, const std::string &appName, QWidget *parent);
~Private() override;
private:
void start();
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const KeyGenerationResult &result, const QByteArray &request);
QUrl saveRequest(const QByteArray &request);
void ensureDialogCreated();
private:
std::string appName;
std::string keyRef;
KeyUsage keyUsage;
QPointer<CreateCSRForCardKeyDialog> dialog;
};
CreateCSRForCardKeyCommand::Private *CreateCSRForCardKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CreateCSRForCardKeyCommand::Private *CreateCSRForCardKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CreateCSRForCardKeyCommand::Private::Private(CreateCSRForCardKeyCommand *qq,
const std::string &keyRef_,
const std::string &serialNumber,
const std::string &appName_,
QWidget *parent)
: CardCommand::Private(qq, serialNumber, parent)
, appName(appName_)
, keyRef(keyRef_)
{
}
CreateCSRForCardKeyCommand::Private::~Private()
{
}
namespace
{
KeyUsage getKeyUsage(const KeyPairInfo &keyInfo)
{
// note: gpgsm does not support creating CSRs for authentication certificates
KeyUsage usage;
if (keyInfo.canCertify()) {
usage.setCanCertify(true);
}
if (keyInfo.canSign()) {
usage.setCanSign(true);
}
if (keyInfo.canEncrypt()) {
usage.setCanEncrypt(true);
}
return usage;
}
}
void CreateCSRForCardKeyCommand::Private::start()
{
if (appName != NetKeyCard::AppName && appName != OpenPGPCard::AppName && appName != PIVCard::AppName) {
qCWarning(KLEOPATRA_LOG) << "CreateCSRForCardKeyCommand does not support card application" << QString::fromStdString(appName);
finished();
return;
}
const auto card = ReaderStatus::instance()->getCard(serialNumber(), appName);
if (!card) {
error(i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
const KeyPairInfo &keyInfo = card->keyInfo(keyRef);
keyUsage = getKeyUsage(keyInfo);
ensureDialogCreated();
dialog->setWindowTitle(i18nc("@title:window", "Certificate Details"));
if (!card->cardHolder().isEmpty()) {
dialog->setName(card->cardHolder());
}
dialog->show();
}
void CreateCSRForCardKeyCommand::Private::slotDialogAccepted()
{
const Error err = ReaderStatus::switchCardAndApp(serialNumber(), appName);
if (err) {
finished();
return;
}
const auto backend = smime();
if (!backend) {
finished();
return;
}
KeyGenerationJob *const job = backend->keyGenerationJob();
if (!job) {
finished();
return;
}
Job::context(job)->setArmor(true);
connect(job, &KeyGenerationJob::result, q, [this](const GpgME::KeyGenerationResult &result, const QByteArray &pubKeyData) {
slotResult(result, pubKeyData);
});
KeyParameters keyParameters(KeyParameters::CMS);
keyParameters.setCardKeyRef(QString::fromStdString(keyRef));
keyParameters.setKeyUsage(keyUsage);
keyParameters.setDN(dialog->dn());
keyParameters.setEmail(dialog->email());
if (const Error err = job->start(keyParameters.toString())) {
error(i18nc("@info", "Creating a CSR for the card key failed:\n%1", Formatting::errorAsString(err)));
finished();
}
}
void CreateCSRForCardKeyCommand::Private::slotDialogRejected()
{
canceled();
}
void CreateCSRForCardKeyCommand::Private::slotResult(const KeyGenerationResult &result, const QByteArray &request)
{
if (result.error().isCanceled()) {
// do nothing
} else if (result.error()) {
error(i18nc("@info", "Creating a CSR for the card key failed:\n%1", Formatting::errorAsString(result.error())));
} else {
const QUrl url = saveRequest(request);
if (!url.isEmpty()) {
information(xi18nc("@info",
"<para>Successfully wrote request to <filename>%1</filename>.</para>"
"<para>You should now send the request to the Certification Authority (CA).</para>",
url.toLocalFile()),
i18nc("@title", "Request Saved"));
}
}
finished();
}
namespace
{
struct SaveToFileResult {
QUrl url;
QString errorMessage;
};
SaveToFileResult saveRequestToFile(const QString &filename, const QByteArray &request, QIODevice::OpenMode mode)
{
QFile file(filename);
if (file.open(mode)) {
const auto bytesWritten = file.write(request);
if (bytesWritten < request.size()) {
return {QUrl(), file.errorString()};
}
return {QUrl::fromLocalFile(file.fileName()), QString()};
}
return {QUrl(), file.errorString()};
}
}
QUrl CreateCSRForCardKeyCommand::Private::saveRequest(const QByteArray &request)
{
const QString proposedFilename = QLatin1StringView("request_%1.p10").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_HHmmss")));
while (true) {
const QString filePath = FileDialog::getSaveFileNameEx(parentWidgetOrView(),
i18nc("@title", "Save Request"),
QStringLiteral("save_csr"),
proposedFilename,
i18n("PKCS#10 Requests (*.p10)"));
if (filePath.isEmpty()) {
// user canceled the dialog
return QUrl();
}
const auto result = saveRequestToFile(filePath, request, QIODevice::NewOnly);
if (result.url.isEmpty()) {
qCDebug(KLEOPATRA_LOG) << "Writing request to file" << filePath << "failed:" << result.errorMessage;
error(xi18nc("@info", "<para>Saving the request failed.</para><para><message>%1</message></para>", result.errorMessage),
i18nc("@title", "Error Saving Request"));
} else {
return result.url;
}
}
}
void CreateCSRForCardKeyCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new CreateCSRForCardKeyDialog;
applyWindowID(dialog);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, q, [this]() {
slotDialogAccepted();
});
connect(dialog, &QDialog::rejected, q, [this]() {
slotDialogRejected();
});
}
CreateCSRForCardKeyCommand::CreateCSRForCardKeyCommand(const std::string &keyRef, const std::string &serialNumber, const std::string &appName, QWidget *parent)
: CardCommand(new Private(this, keyRef, serialNumber, appName, parent))
{
}
CreateCSRForCardKeyCommand::~CreateCSRForCardKeyCommand()
{
}
void CreateCSRForCardKeyCommand::doStart()
{
d->start();
}
void CreateCSRForCardKeyCommand::doCancel()
{
}
#undef d
#undef q
#include "moc_createcsrforcardkeycommand.cpp"
diff --git a/src/commands/newopenpgpcertificatecommand.cpp b/src/commands/newopenpgpcertificatecommand.cpp
index 490cf3aaa..3a6eddedf 100644
--- a/src/commands/newopenpgpcertificatecommand.cpp
+++ b/src/commands/newopenpgpcertificatecommand.cpp
@@ -1,273 +1,273 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/newopenpgpcertificatecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "newopenpgpcertificatecommand.h"
#include "command_p.h"
-#include "dialogs/newopenpgpcertificatedetailsdialog.h"
#include "kleopatraapplication.h"
#include "utils/emptypassphraseprovider.h"
-#include "utils/keyparameters.h"
#include "utils/userinfo.h"
#include <settings.h>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
+#include <Libkleo/KeyParameters>
+#include <Libkleo/OpenPGPCertificateCreationDialog>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
#include <QProgressDialog>
#include <QSettings>
#include <gpgme++/context.h>
#include <gpgme++/keygenerationresult.h>
#include <kleopatra_debug.h>
using namespace Kleo;
using namespace GpgME;
class NewOpenPGPCertificateCommand::Private : public Command::Private
{
friend class ::Kleo::NewOpenPGPCertificateCommand;
NewOpenPGPCertificateCommand *q_func() const
{
return static_cast<NewOpenPGPCertificateCommand *>(q);
}
public:
explicit Private(NewOpenPGPCertificateCommand *qq, KeyListController *c)
: Command::Private{qq, c}
{
}
void getCertificateDetails();
void createCertificate();
void showResult(const KeyGenerationResult &result);
void showErrorDialog(const KeyGenerationResult &result);
private:
KeyParameters keyParameters;
bool protectKeyWithPassword = false;
EmptyPassphraseProvider emptyPassphraseProvider;
- QPointer<NewOpenPGPCertificateDetailsDialog> detailsDialog;
+ QPointer<OpenPGPCertificateCreationDialog> detailsDialog;
QPointer<QGpgME::Job> job;
QPointer<QProgressDialog> progressDialog;
};
NewOpenPGPCertificateCommand::Private *NewOpenPGPCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const NewOpenPGPCertificateCommand::Private *NewOpenPGPCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
void NewOpenPGPCertificateCommand::Private::getCertificateDetails()
{
- detailsDialog = new NewOpenPGPCertificateDetailsDialog;
+ detailsDialog = new OpenPGPCertificateCreationDialog;
detailsDialog->setAttribute(Qt::WA_DeleteOnClose);
applyWindowID(detailsDialog);
if (keyParameters.protocol() == KeyParameters::NoProtocol) {
const auto settings = Kleo::Settings{};
const KConfigGroup config{KSharedConfig::openConfig(), QLatin1StringView("CertificateCreationWizard")};
// prefer the last used name and email address over the values retrieved from the system
detailsDialog->setName(config.readEntry("NAME", QString{}));
if (detailsDialog->name().isEmpty() && settings.prefillName()) {
detailsDialog->setName(userFullName());
}
detailsDialog->setEmail(config.readEntry("EMAIL", QString{}));
if (detailsDialog->email().isEmpty() && settings.prefillEmail()) {
detailsDialog->setEmail(userEmailAddress());
}
} else {
detailsDialog->setKeyParameters(keyParameters);
detailsDialog->setProtectKeyWithPassword(protectKeyWithPassword);
}
connect(detailsDialog, &QDialog::accepted, q, [this]() {
keyParameters = detailsDialog->keyParameters();
protectKeyWithPassword = detailsDialog->protectKeyWithPassword();
QMetaObject::invokeMethod(
q,
[this] {
createCertificate();
},
Qt::QueuedConnection);
});
connect(detailsDialog, &QDialog::rejected, q, [this]() {
canceled();
});
detailsDialog->show();
}
void NewOpenPGPCertificateCommand::Private::createCertificate()
{
Q_ASSERT(keyParameters.protocol() == KeyParameters::OpenPGP);
auto keyGenJob = QGpgME::openpgp()->keyGenerationJob();
if (!keyGenJob) {
finished();
return;
}
if (!protectKeyWithPassword) {
auto ctx = QGpgME::Job::context(keyGenJob);
ctx->setPassphraseProvider(&emptyPassphraseProvider);
ctx->setPinentryMode(Context::PinentryLoopback);
}
auto settings = KleopatraApplication::instance()->distributionSettings();
if (settings) {
keyParameters.setComment(settings->value(QStringLiteral("uidcomment"), {}).toString());
}
connect(keyGenJob, &QGpgME::KeyGenerationJob::result, q, [this](const KeyGenerationResult &result) {
QMetaObject::invokeMethod(
q,
[this, result] {
showResult(result);
},
Qt::QueuedConnection);
});
if (const Error err = keyGenJob->start(keyParameters.toString())) {
error(i18n("Could not start key pair creation: %1", Formatting::errorAsString(err)));
finished();
return;
} else {
job = keyGenJob;
}
progressDialog = new QProgressDialog;
progressDialog->setAttribute(Qt::WA_DeleteOnClose);
applyWindowID(progressDialog);
progressDialog->setModal(true);
progressDialog->setWindowTitle(i18nc("@title", "Creating Key Pair..."));
progressDialog->setLabelText(i18n("The process of creating a key requires large amounts of random numbers. This may require several minutes..."));
progressDialog->setRange(0, 0);
connect(progressDialog, &QProgressDialog::canceled, job, &QGpgME::Job::slotCancel);
connect(job, &QGpgME::Job::done, q, [this]() {
if (progressDialog) {
progressDialog->accept();
}
});
progressDialog->show();
}
void NewOpenPGPCertificateCommand::Private::showResult(const KeyGenerationResult &result)
{
if (result.error().isCanceled()) {
finished();
return;
}
// Ensure that we have the key in the cache
Key key;
if (!result.error().code() && result.fingerprint()) {
std::unique_ptr<Context> ctx{Context::createForProtocol(OpenPGP)};
if (ctx) {
Error err;
key = ctx->key(result.fingerprint(), err, /*secret=*/true);
if (!key.isNull()) {
KeyCache::mutableInstance()->insert(key);
}
}
}
if (!key.isNull()) {
success(
xi18n("<para>A new OpenPGP certificate was created successfully.</para>"
"<para>Fingerprint of the new certificate: %1</para>",
Formatting::prettyID(key.primaryFingerprint())));
finished();
} else {
showErrorDialog(result);
}
}
void NewOpenPGPCertificateCommand::Private::showErrorDialog(const KeyGenerationResult &result)
{
QString text;
if (result.error() || !result.fingerprint()) {
text = xi18n(
"<para>The creation of a new OpenPGP certificate failed.</para>"
"<para>Error: <message>%1</message></para>",
Formatting::errorAsString(result.error()));
} else {
// no error and we have a fingerprint, but there was no corresponding key in the key ring
text = xi18n(
"<para>A new OpenPGP certificate was created successfully, but it has not been found in the key ring.</para>"
"<para>Fingerprint of the new certificate:<nl/>%1</para>",
Formatting::prettyID(result.fingerprint()));
}
auto dialog = new QDialog;
applyWindowID(dialog);
dialog->setWindowTitle(i18nc("@title:window", "Error"));
auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Retry | QDialogButtonBox::Ok, dialog};
const auto buttonCode = KMessageBox::createKMessageBox(dialog, buttonBox, QMessageBox::Critical, text, {}, {}, nullptr, {});
if (buttonCode == QDialogButtonBox::Retry) {
QMetaObject::invokeMethod(
q,
[this]() {
getCertificateDetails();
},
Qt::QueuedConnection);
} else {
finished();
}
}
NewOpenPGPCertificateCommand::NewOpenPGPCertificateCommand()
: NewOpenPGPCertificateCommand(nullptr, nullptr)
{
}
NewOpenPGPCertificateCommand::NewOpenPGPCertificateCommand(QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
}
NewOpenPGPCertificateCommand::~NewOpenPGPCertificateCommand() = default;
void NewOpenPGPCertificateCommand::doStart()
{
d->getCertificateDetails();
}
void NewOpenPGPCertificateCommand::doCancel()
{
if (d->detailsDialog) {
d->detailsDialog->close();
}
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_newopenpgpcertificatecommand.cpp"
diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp
index 2783938a8..72dbadde7 100644
--- a/src/crypto/gui/certificatelineedit.cpp
+++ b/src/crypto/gui/certificatelineedit.cpp
@@ -1,890 +1,890 @@
/* crypto/gui/certificatelineedit.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatelineedit.h"
#include "commands/detailscommand.h"
#include "dialogs/groupdetailsdialog.h"
#include "utils/accessibility.h"
-#include "view/errorlabel.h"
#include <QAccessible>
#include <QAction>
#include <QCompleter>
#include <QPushButton>
#include <QSignalBlocker>
#include "kleopatra_debug.h"
#include <Libkleo/Debug>
+#include <Libkleo/ErrorLabel>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilter>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyList>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/UserIDProxyModel>
#include <KLocalizedString>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QMenu>
#include <QToolButton>
using namespace Kleo;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
Q_DECLARE_METATYPE(KeyGroup)
static QStringList s_lookedUpKeys;
namespace
{
class CompletionProxyModel : public KeyListSortFilterProxyModel
{
Q_OBJECT
public:
CompletionProxyModel(QObject *parent = nullptr)
: KeyListSortFilterProxyModel(parent)
{
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent)
// pretend that there is only one column to workaround a bug in
// QAccessibleTable which provides the accessibility interface for the
// completion pop-up
return 1;
}
QVariant data(const QModelIndex &idx, int role) const override
{
if (!idx.isValid()) {
return QVariant();
}
switch (role) {
case Qt::DecorationRole: {
const auto key = KeyListSortFilterProxyModel::data(idx, KeyList::KeyRole).value<GpgME::Key>();
if (!key.isNull()) {
return Kleo::Formatting::iconForUid(key.userID(0));
}
const auto userID = KeyListSortFilterProxyModel::data(idx, KeyList::UserIDRole).value<GpgME::UserID>();
if (!userID.isNull()) {
return Kleo::Formatting::iconForUid(userID);
}
const auto group = KeyListSortFilterProxyModel::data(idx, KeyList::GroupRole).value<KeyGroup>();
if (!group.isNull()) {
return QIcon::fromTheme(QStringLiteral("group"));
}
Q_ASSERT(!key.isNull() || !userID.isNull() || !group.isNull());
return QVariant();
}
default:
return KeyListSortFilterProxyModel::data(index(idx.row(), KeyList::Summary), role);
}
}
private:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
{
const auto leftKey = sourceModel()->data(left, KeyList::KeyRole).value<GpgME::Key>();
const auto leftGroup = leftKey.isNull() ? sourceModel()->data(left, KeyList::GroupRole).value<KeyGroup>() : KeyGroup{};
const auto leftUserID = sourceModel()->data(left, KeyList::UserIDRole).value<GpgME::UserID>();
const auto rightUserID = sourceModel()->data(right, KeyList::UserIDRole).value<GpgME::UserID>();
const auto rightKey = sourceModel()->data(right, KeyList::KeyRole).value<GpgME::Key>();
const auto rightGroup = rightKey.isNull() ? sourceModel()->data(right, KeyList::GroupRole).value<KeyGroup>() : KeyGroup{};
// shouldn't happen, but still put null entries at the end
if (leftKey.isNull() && leftUserID.isNull() && leftGroup.isNull()) {
return false;
}
if (rightKey.isNull() && rightUserID.isNull() && rightGroup.isNull()) {
return true;
}
// first sort by the displayed name and/or email address
const auto leftNameAndOrEmail = leftGroup.isNull()
? (leftKey.isNull() ? Formatting::nameAndEmailForSummaryLine(leftUserID) : Formatting::nameAndEmailForSummaryLine(leftKey))
: leftGroup.name();
const auto rightNameAndOrEmail = rightGroup.isNull()
? (rightKey.isNull() ? Formatting::nameAndEmailForSummaryLine(rightUserID) : Formatting::nameAndEmailForSummaryLine(rightKey))
: rightGroup.name();
const int cmp = QString::localeAwareCompare(leftNameAndOrEmail, rightNameAndOrEmail);
if (cmp) {
return cmp < 0;
}
// then sort groups before certificates
if (!leftGroup.isNull() && (!rightKey.isNull() || !rightUserID.isNull())) {
return true; // left is group, right is certificate
}
if ((!leftKey.isNull() || !rightKey.isNull()) && !rightGroup.isNull()) {
return false; // left is certificate, right is group
}
// if both are groups (with identical names) sort them by their ID
if (!leftGroup.isNull() && !rightGroup.isNull()) {
return leftGroup.id() < rightGroup.id();
}
// sort certificates with same name/email by validity and creation time
const auto lUid = leftUserID.isNull() ? leftKey.userID(0) : leftUserID;
const auto rUid = rightUserID.isNull() ? rightKey.userID(0) : rightUserID;
if (lUid.validity() != rUid.validity()) {
return lUid.validity() > rUid.validity();
}
/* Both have the same validity, check which one is newer. */
time_t leftTime = 0;
for (const GpgME::Subkey &s : (leftUserID.isNull() ? leftKey : leftUserID.parent()).subkeys()) {
if (s.isBad()) {
continue;
}
if (s.creationTime() > leftTime) {
leftTime = s.creationTime();
}
}
time_t rightTime = 0;
for (const GpgME::Subkey &s : (rightUserID.isNull() ? rightKey : rightUserID.parent()).subkeys()) {
if (s.isBad()) {
continue;
}
if (s.creationTime() > rightTime) {
rightTime = s.creationTime();
}
}
if (rightTime != leftTime) {
return leftTime > rightTime;
}
// as final resort we compare the fingerprints
return strcmp((leftUserID.isNull() ? leftKey : leftUserID.parent()).primaryFingerprint(),
(rightUserID.isNull() ? rightKey : rightUserID.parent()).primaryFingerprint())
< 0;
}
};
auto createSeparatorAction(QObject *parent)
{
auto action = new QAction{parent};
action->setSeparator(true);
return action;
}
} // namespace
class CertificateLineEdit::Private
{
CertificateLineEdit *q;
public:
enum class Status {
Empty, //< text is empty
Success, //< a certificate or group is set
None, //< entered text does not match any certificates or groups
Ambiguous, //< entered text matches multiple certificates or groups
};
enum class CursorPositioning {
MoveToEnd,
KeepPosition,
MoveToStart,
Default = MoveToEnd,
};
explicit Private(CertificateLineEdit *qq, AbstractKeyListModel *model, KeyUsage::Flags usage, KeyFilter *filter);
QString text() const;
void setKey(const GpgME::Key &key);
void setGroup(const KeyGroup &group);
void setUserID(const GpgME::UserID &userID);
void setKeyFilter(const std::shared_ptr<KeyFilter> &filter);
void setAccessibleName(const QString &s);
private:
void updateKey(CursorPositioning positioning);
void editChanged();
void editFinished();
void checkLocate();
void onLocateJobResult(QGpgME::Job *job, const QString &email, const KeyListResult &result, const std::vector<GpgME::Key> &keys);
void openDetailsDialog();
void setTextWithBlockedSignals(const QString &s, CursorPositioning positioning);
void showContextMenu(const QPoint &pos);
QString errorMessage() const;
QIcon statusIcon() const;
QString statusToolTip() const;
void updateStatusAction();
void updateErrorLabel();
void updateAccessibleNameAndDescription();
public:
Status mStatus = Status::Empty;
bool mEditingInProgress = false;
GpgME::Key mKey;
KeyGroup mGroup;
GpgME::UserID mUserId;
struct Ui {
explicit Ui(QWidget *parent)
: lineEdit{parent}
, button{parent}
, errorLabel{parent}
{
}
QLineEdit lineEdit;
QToolButton button;
ErrorLabel errorLabel;
} ui;
private:
QString mAccessibleName;
UserIDProxyModel *const mUserIdProxyModel;
KeyListSortFilterProxyModel *const mFilterModel;
CompletionProxyModel *const mCompleterFilterModel;
QCompleter *mCompleter = nullptr;
std::shared_ptr<KeyFilter> mFilter;
QAction *const mStatusAction;
QAction *const mShowDetailsAction;
QPointer<QGpgME::Job> mLocateJob;
Formatting::IconProvider mIconProvider;
};
CertificateLineEdit::Private::Private(CertificateLineEdit *qq, AbstractKeyListModel *model, KeyUsage::Flags usage, KeyFilter *filter)
: q{qq}
, ui{qq}
, mUserIdProxyModel{new UserIDProxyModel{qq}}
, mFilterModel{new KeyListSortFilterProxyModel{qq}}
, mCompleterFilterModel{new CompletionProxyModel{qq}}
, mCompleter{new QCompleter{qq}}
, mFilter{std::shared_ptr<KeyFilter>{filter}}
, mStatusAction{new QAction{qq}}
, mShowDetailsAction{new QAction{qq}}
, mIconProvider{usage}
{
ui.lineEdit.setPlaceholderText(i18nc("@info:placeholder", "Please enter a name or email address..."));
ui.lineEdit.setClearButtonEnabled(true);
ui.lineEdit.setContextMenuPolicy(Qt::CustomContextMenu);
ui.lineEdit.addAction(mStatusAction, QLineEdit::LeadingPosition);
mUserIdProxyModel->setSourceModel(model);
mCompleterFilterModel->setKeyFilter(mFilter);
mCompleterFilterModel->setSourceModel(mUserIdProxyModel);
// initialize dynamic sorting
mCompleterFilterModel->sort(0);
mCompleter->setModel(mCompleterFilterModel);
mCompleter->setFilterMode(Qt::MatchContains);
mCompleter->setCaseSensitivity(Qt::CaseInsensitive);
ui.lineEdit.setCompleter(mCompleter);
ui.button.setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new")));
ui.button.setToolTip(i18nc("@info:tooltip", "Show certificate list"));
ui.button.setAccessibleName(i18n("Show certificate list"));
ui.errorLabel.setVisible(false);
auto vbox = new QVBoxLayout{q};
vbox->setContentsMargins(0, 0, 0, 0);
auto l = new QHBoxLayout;
l->setContentsMargins(0, 0, 0, 0);
l->addWidget(&ui.lineEdit);
l->addWidget(&ui.button);
vbox->addLayout(l);
vbox->addWidget(&ui.errorLabel);
q->setFocusPolicy(ui.lineEdit.focusPolicy());
q->setFocusProxy(&ui.lineEdit);
mShowDetailsAction->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
mShowDetailsAction->setText(i18nc("@action:inmenu", "Show Details"));
mShowDetailsAction->setEnabled(false);
mFilterModel->setSourceModel(mUserIdProxyModel);
mFilterModel->setFilterKeyColumn(KeyList::Summary);
if (filter) {
mFilterModel->setKeyFilter(mFilter);
}
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, q, [this]() {
updateKey(CursorPositioning::KeepPosition);
});
connect(KeyCache::instance().get(), &Kleo::KeyCache::groupUpdated, q, [this](const KeyGroup &group) {
if (!mGroup.isNull() && mGroup.source() == group.source() && mGroup.id() == group.id()) {
setTextWithBlockedSignals(Formatting::summaryLine(group), CursorPositioning::KeepPosition);
// queue the update to ensure that the model has been updated
QMetaObject::invokeMethod(
q,
[this]() {
updateKey(CursorPositioning::KeepPosition);
},
Qt::QueuedConnection);
}
});
connect(KeyCache::instance().get(), &Kleo::KeyCache::groupRemoved, q, [this](const KeyGroup &group) {
if (!mGroup.isNull() && mGroup.source() == group.source() && mGroup.id() == group.id()) {
mGroup = KeyGroup();
QSignalBlocker blocky{&ui.lineEdit};
ui.lineEdit.clear();
// queue the update to ensure that the model has been updated
QMetaObject::invokeMethod(
q,
[this]() {
updateKey(CursorPositioning::KeepPosition);
},
Qt::QueuedConnection);
}
});
connect(&ui.lineEdit, &QLineEdit::editingFinished, q, [this]() {
// queue the call of editFinished() to ensure that QCompleter::activated is handled first
QMetaObject::invokeMethod(
q,
[this]() {
editFinished();
},
Qt::QueuedConnection);
});
connect(&ui.lineEdit, &QLineEdit::textChanged, q, [this]() {
editChanged();
});
connect(&ui.lineEdit, &QLineEdit::customContextMenuRequested, q, [this](const QPoint &pos) {
showContextMenu(pos);
});
connect(mStatusAction, &QAction::triggered, q, [this]() {
openDetailsDialog();
});
connect(mShowDetailsAction, &QAction::triggered, q, [this]() {
openDetailsDialog();
});
connect(&ui.button, &QToolButton::clicked, q, &CertificateLineEdit::certificateSelectionRequested);
connect(mCompleter, qOverload<const QModelIndex &>(&QCompleter::activated), q, [this](const QModelIndex &index) {
Key key = mCompleter->completionModel()->data(index, KeyList::KeyRole).value<Key>();
auto group = mCompleter->completionModel()->data(index, KeyList::GroupRole).value<KeyGroup>();
auto userID = mCompleter->completionModel()->data(index, KeyList::UserIDRole).value<UserID>();
if (!userID.isNull()) {
q->setUserID(userID);
} else if (!key.isNull()) {
q->setKey(key);
} else if (!group.isNull()) {
q->setGroup(group);
} else {
qCDebug(KLEOPATRA_LOG) << "Activated item is neither key, nor userid, or group";
}
// queue the call of editFinished() to ensure that QLineEdit finished its own work
QMetaObject::invokeMethod(
q,
[this]() {
editFinished();
},
Qt::QueuedConnection);
});
updateKey(CursorPositioning::Default);
}
void CertificateLineEdit::Private::openDetailsDialog()
{
if (!q->key().isNull()) {
auto cmd = new Commands::DetailsCommand{q->key()};
cmd->setParentWidget(q);
cmd->start();
} else if (!q->group().isNull()) {
auto dlg = new Dialogs::GroupDetailsDialog{q};
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setGroup(q->group());
dlg->show();
}
}
void CertificateLineEdit::Private::setTextWithBlockedSignals(const QString &s, CursorPositioning positioning)
{
QSignalBlocker blocky{&ui.lineEdit};
const auto cursorPos = ui.lineEdit.cursorPosition();
ui.lineEdit.setText(s);
switch (positioning) {
case CursorPositioning::KeepPosition:
ui.lineEdit.setCursorPosition(cursorPos);
break;
case CursorPositioning::MoveToStart:
ui.lineEdit.setCursorPosition(0);
break;
case CursorPositioning::MoveToEnd:
default:; // setText() already moved the cursor to the end of the line
};
}
void CertificateLineEdit::Private::showContextMenu(const QPoint &pos)
{
if (QMenu *menu = ui.lineEdit.createStandardContextMenu()) {
auto *const firstStandardAction = menu->actions().value(0);
menu->insertActions(firstStandardAction, {mShowDetailsAction, createSeparatorAction(menu)});
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->popup(ui.lineEdit.mapToGlobal(pos));
}
}
CertificateLineEdit::CertificateLineEdit(AbstractKeyListModel *model, KeyUsage::Flags usage, KeyFilter *filter, QWidget *parent)
: QWidget{parent}
, d{new Private{this, model, usage, filter}}
{
/* Take ownership of the model to prevent double deletion when the
* filter models are deleted */
model->setParent(parent ? parent : this);
}
CertificateLineEdit::~CertificateLineEdit() = default;
void CertificateLineEdit::Private::editChanged()
{
const bool editingStarted = !mEditingInProgress;
mEditingInProgress = true;
updateKey(CursorPositioning::Default);
if (editingStarted) {
Q_EMIT q->editingStarted();
}
if (q->isEmpty()) {
Q_EMIT q->cleared();
}
}
void CertificateLineEdit::Private::editFinished()
{
// perform a first update with the "editing in progress" flag still set
updateKey(CursorPositioning::MoveToStart);
mEditingInProgress = false;
checkLocate();
// perform another update with the "editing in progress" flag cleared
// after a key locate may have been started; this makes sure that displaying
// an error is delayed until the key locate job has finished
updateKey(CursorPositioning::MoveToStart);
}
void CertificateLineEdit::Private::checkLocate()
{
if (mStatus != Status::None) {
// try to locate key only if text matches no local certificates, user ids, or groups
return;
}
// Only check once per mailbox
const auto mailText = ui.lineEdit.text().trimmed();
if (mailText.isEmpty() || s_lookedUpKeys.contains(mailText)) {
return;
}
s_lookedUpKeys << mailText;
if (mLocateJob) {
mLocateJob->slotCancel();
mLocateJob.clear();
}
auto job = QGpgME::openpgp()->locateKeysJob();
connect(job, &QGpgME::KeyListJob::result, q, [this, job, mailText](const KeyListResult &result, const std::vector<GpgME::Key> &keys) {
onLocateJobResult(job, mailText, result, keys);
});
if (auto err = job->start({mailText}, /*secretOnly=*/false)) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Error: Starting" << job << "for" << mailText << "failed with" << Formatting::errorAsString(err);
} else {
mLocateJob = job;
qCDebug(KLEOPATRA_LOG) << __func__ << "Started" << job << "for" << mailText;
}
}
void CertificateLineEdit::Private::onLocateJobResult(QGpgME::Job *job, const QString &email, const KeyListResult &result, const std::vector<GpgME::Key> &keys)
{
if (mLocateJob != job) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Ignoring outdated finished" << job << "for" << email;
return;
}
qCDebug(KLEOPATRA_LOG) << __func__ << job << "for" << email << "finished with" << Formatting::errorAsString(result.error()) << "and keys" << keys;
mLocateJob.clear();
if (!keys.empty() && !keys.front().isNull()) {
KeyCache::mutableInstance()->insert(keys.front());
// inserting the key implicitly triggers an update
} else {
// explicitly trigger an update to display "no key" error
updateKey(CursorPositioning::MoveToStart);
}
}
void CertificateLineEdit::Private::updateKey(CursorPositioning positioning)
{
static const _detail::ByFingerprint<std::equal_to> keysHaveSameFingerprint;
const auto mailText = ui.lineEdit.text().trimmed();
auto newKey = Key();
auto newGroup = KeyGroup();
auto newUserId = UserID();
if (mailText.isEmpty()) {
mStatus = Status::Empty;
} else {
mFilterModel->setFilterRegularExpression(QRegularExpression::escape(mailText));
if (mFilterModel->rowCount() > 1) {
// keep current key, user id, or group if they still match
if (!mKey.isNull()) {
for (int row = 0; row < mFilterModel->rowCount(); ++row) {
const QModelIndex index = mFilterModel->index(row, 0);
Key key = mFilterModel->key(index);
if (!key.isNull() && keysHaveSameFingerprint(key, mKey)) {
newKey = mKey;
break;
}
}
} else if (!mGroup.isNull()) {
newGroup = mGroup;
for (int row = 0; row < mFilterModel->rowCount(); ++row) {
const QModelIndex index = mFilterModel->index(row, 0);
KeyGroup group = mFilterModel->group(index);
if (!group.isNull() && group.source() == mGroup.source() && group.id() == mGroup.id()) {
newGroup = mGroup;
break;
}
}
} else if (!mUserId.isNull()) {
for (int row = 0; row < mFilterModel->rowCount(); ++row) {
const QModelIndex index = mFilterModel->index(row, 0);
UserID userId = index.data(KeyList::UserIDRole).value<UserID>();
if (!userId.isNull() && keysHaveSameFingerprint(userId.parent(), mUserId.parent()) && !strcmp(userId.id(), mUserId.id())) {
newUserId = mUserId;
}
}
}
if (newKey.isNull() && newGroup.isNull() && newUserId.isNull()) {
mStatus = Status::Ambiguous;
}
} else if (mFilterModel->rowCount() == 1) {
const auto index = mFilterModel->index(0, 0);
newUserId = mFilterModel->data(index, KeyList::UserIDRole).value<UserID>();
if (newUserId.isNull()) {
newKey = mFilterModel->data(index, KeyList::KeyRole).value<Key>();
}
newGroup = mFilterModel->data(index, KeyList::GroupRole).value<KeyGroup>();
Q_ASSERT(!newKey.isNull() || !newGroup.isNull() || !newUserId.isNull());
if (newKey.isNull() && newGroup.isNull() && newUserId.isNull()) {
mStatus = Status::None;
}
} else {
mStatus = Status::None;
}
}
mKey = newKey;
mGroup = newGroup;
mUserId = newUserId;
if (!mKey.isNull()) {
/* FIXME: This needs to be solved by a multiple UID supporting model */
mStatus = Status::Success;
ui.lineEdit.setToolTip(Formatting::toolTip(mKey, Formatting::ToolTipOption::AllOptions));
if (!mEditingInProgress) {
setTextWithBlockedSignals(Formatting::summaryLine(mKey), positioning);
}
} else if (!mGroup.isNull()) {
mStatus = Status::Success;
ui.lineEdit.setToolTip(Formatting::toolTip(mGroup, Formatting::ToolTipOption::AllOptions));
if (!mEditingInProgress) {
setTextWithBlockedSignals(Formatting::summaryLine(mGroup), positioning);
}
} else if (!mUserId.isNull()) {
mStatus = Status::Success;
ui.lineEdit.setToolTip(Formatting::toolTip(mUserId, Formatting::ToolTipOption::AllOptions));
if (!mEditingInProgress) {
setTextWithBlockedSignals(Formatting::summaryLine(mUserId), positioning);
}
} else {
ui.lineEdit.setToolTip({});
}
mShowDetailsAction->setEnabled(mStatus == Status::Success);
updateStatusAction();
updateErrorLabel();
Q_EMIT q->keyChanged();
}
QString CertificateLineEdit::Private::errorMessage() const
{
switch (mStatus) {
case Status::Empty:
case Status::Success:
return {};
case Status::None:
return i18n("No matching certificates or groups found");
case Status::Ambiguous:
return i18n("Multiple matching certificates or groups found");
default:
qDebug(KLEOPATRA_LOG) << __func__ << "Invalid status:" << static_cast<int>(mStatus);
Q_ASSERT(!"Invalid status");
};
return {};
}
QIcon CertificateLineEdit::Private::statusIcon() const
{
switch (mStatus) {
case Status::Empty:
return QIcon::fromTheme(QStringLiteral("emblem-unavailable"));
case Status::Success:
if (!mKey.isNull()) {
return mIconProvider.icon(mKey);
} else if (!mGroup.isNull()) {
return mIconProvider.icon(mGroup);
} else if (!mUserId.isNull()) {
return mIconProvider.icon(mUserId);
} else {
qDebug(KLEOPATRA_LOG) << __func__ << "Success, but neither key, nor user id, or group.";
return {};
}
case Status::None:
case Status::Ambiguous:
if (mEditingInProgress || mLocateJob) {
return QIcon::fromTheme(QStringLiteral("emblem-question"));
} else {
return QIcon::fromTheme(QStringLiteral("emblem-error"));
}
default:
qDebug(KLEOPATRA_LOG) << __func__ << "Invalid status:" << static_cast<int>(mStatus);
Q_ASSERT(!"Invalid status");
};
return {};
}
QString CertificateLineEdit::Private::statusToolTip() const
{
switch (mStatus) {
case Status::Empty:
return {};
case Status::Success:
if (!mUserId.isNull()) {
return Formatting::validity(mUserId);
}
if (!mKey.isNull()) {
return Formatting::validity(mKey.userID(0));
} else if (!mGroup.isNull()) {
return Formatting::validity(mGroup);
} else {
qDebug(KLEOPATRA_LOG) << __func__ << "Success, but neither key, nor user id, or group.";
return {};
}
case Status::None:
case Status::Ambiguous:
return errorMessage();
default:
qDebug(KLEOPATRA_LOG) << __func__ << "Invalid status:" << static_cast<int>(mStatus);
Q_ASSERT(!"Invalid status");
};
return {};
}
void CertificateLineEdit::Private::updateStatusAction()
{
mStatusAction->setIcon(statusIcon());
mStatusAction->setToolTip(statusToolTip());
}
namespace
{
QString decoratedError(const QString &text)
{
return text.isEmpty() ? QString() : i18nc("@info", "Error: %1", text);
}
}
void CertificateLineEdit::Private::updateErrorLabel()
{
const auto currentErrorMessage = ui.errorLabel.text();
const auto newErrorMessage = decoratedError(errorMessage());
if (newErrorMessage == currentErrorMessage) {
return;
}
if (currentErrorMessage.isEmpty() && (mEditingInProgress || mLocateJob)) {
// delay showing the error message until editing is finished, so that we
// do not annoy the user with an error message while they are still
// entering the recipient;
// on the other hand, we clear the error message immediately if it does
// not apply anymore and we update the error message immediately if it
// changed
return;
}
ui.errorLabel.setVisible(!newErrorMessage.isEmpty());
ui.errorLabel.setText(newErrorMessage);
updateAccessibleNameAndDescription();
}
void CertificateLineEdit::Private::setAccessibleName(const QString &s)
{
mAccessibleName = s;
updateAccessibleNameAndDescription();
}
void CertificateLineEdit::Private::updateAccessibleNameAndDescription()
{
// fall back to default accessible name if accessible name wasn't set explicitly
if (mAccessibleName.isEmpty()) {
mAccessibleName = getAccessibleName(&ui.lineEdit);
}
const bool errorShown = ui.errorLabel.isVisible();
// Qt does not support "described-by" relations (like WCAG's "aria-describedby" relationship attribute);
// emulate this by setting the error message as accessible description of the input field
const auto description = errorShown ? ui.errorLabel.text() : QString{};
if (ui.lineEdit.accessibleDescription() != description) {
ui.lineEdit.setAccessibleDescription(description);
}
// Qt does not support IA2's "invalid entry" state (like WCAG's "aria-invalid" state attribute);
// screen readers say something like "invalid data" if this state is set;
// emulate this by adding "invalid data" to the accessible name of the input field
const auto name = errorShown ? mAccessibleName + QLatin1StringView{", "} + invalidEntryText() //
: mAccessibleName;
if (ui.lineEdit.accessibleName() != name) {
ui.lineEdit.setAccessibleName(name);
}
}
Key CertificateLineEdit::key() const
{
if (isEnabled()) {
return d->mKey;
} else {
return Key();
}
}
KeyGroup CertificateLineEdit::group() const
{
if (isEnabled()) {
return d->mGroup;
} else {
return KeyGroup();
}
}
UserID CertificateLineEdit::userID() const
{
if (isEnabled()) {
return d->mUserId;
} else {
return UserID();
}
}
QString CertificateLineEdit::Private::text() const
{
return ui.lineEdit.text().trimmed();
}
QString CertificateLineEdit::text() const
{
return d->text();
}
void CertificateLineEdit::Private::setKey(const Key &key)
{
mKey = key;
mGroup = KeyGroup();
mUserId = UserID();
qCDebug(KLEOPATRA_LOG) << "Setting Key. " << Formatting::summaryLine(key);
// position cursor, so that that the start of the summary is visible
setTextWithBlockedSignals(Formatting::summaryLine(key), CursorPositioning::MoveToStart);
updateKey(CursorPositioning::MoveToStart);
}
void CertificateLineEdit::setKey(const Key &key)
{
d->setKey(key);
}
void CertificateLineEdit::Private::setUserID(const UserID &userID)
{
mUserId = userID;
mKey = Key();
mGroup = KeyGroup();
qCDebug(KLEOPATRA_LOG) << "Setting UserID. " << Formatting::summaryLine(userID);
// position cursor, so that the start of the summary is visible
setTextWithBlockedSignals(Formatting::summaryLine(userID), CursorPositioning::MoveToStart);
updateKey(CursorPositioning::MoveToStart);
}
void CertificateLineEdit::setUserID(const UserID &userID)
{
d->setUserID(userID);
}
void CertificateLineEdit::Private::setGroup(const KeyGroup &group)
{
mGroup = group;
mKey = Key();
mUserId = UserID();
const QString summary = Formatting::summaryLine(group);
qCDebug(KLEOPATRA_LOG) << "Setting KeyGroup. " << summary;
// position cursor, so that that the start of the summary is visible
setTextWithBlockedSignals(summary, CursorPositioning::MoveToStart);
updateKey(CursorPositioning::MoveToStart);
}
void CertificateLineEdit::setGroup(const KeyGroup &group)
{
d->setGroup(group);
}
bool CertificateLineEdit::isEmpty() const
{
return d->mStatus == Private::Status::Empty;
}
bool CertificateLineEdit::isEditingInProgress() const
{
return d->mEditingInProgress;
}
bool CertificateLineEdit::hasAcceptableInput() const
{
return d->mStatus == Private::Status::Empty //
|| d->mStatus == Private::Status::Success;
}
void CertificateLineEdit::Private::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
mFilter = filter;
mFilterModel->setKeyFilter(filter);
mCompleterFilterModel->setKeyFilter(mFilter);
updateKey(CursorPositioning::Default);
}
void CertificateLineEdit::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
d->setKeyFilter(filter);
}
void CertificateLineEdit::setAccessibleNameOfLineEdit(const QString &name)
{
d->setAccessibleName(name);
}
#include "certificatelineedit.moc"
#include "moc_certificatelineedit.cpp"
diff --git a/src/dialogs/addsubkeydialog.cpp b/src/dialogs/addsubkeydialog.cpp
index e240d360f..eca791c0c 100644
--- a/src/dialogs/addsubkeydialog.cpp
+++ b/src/dialogs/addsubkeydialog.cpp
@@ -1,272 +1,272 @@
/* -*- mode: c++; c-basic-offset:4 -*-
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021-2023 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "addsubkeydialog.h"
-#include "utils/expiration.h"
#include "utils/gui-helper.h"
#include "utils/scrollarea.h"
#include <Libkleo/Compat>
#include <Libkleo/Compliance>
+#include <Libkleo/Expiration>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <KConfigGroup>
#include <KDateComboBox>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <KStandardGuiItem>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QCheckBox>
#include <QDate>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <QVBoxLayout>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
class AddSubkeyDialog::Private
{
AddSubkeyDialog *const q;
public:
Private(AddSubkeyDialog *qq)
: q{qq}
, ui{qq}
{
}
struct UI {
QComboBox *keyAlgoCB = nullptr;
QRadioButton *signingCB = nullptr;
QRadioButton *encryptionCB = nullptr;
QRadioButton *authenticationCB = nullptr;
QCheckBox *expiryCB = nullptr;
KDateComboBox *expiryDE = nullptr;
QLabel *primaryKeyExpiration = nullptr;
QDialogButtonBox *buttonBox = nullptr;
UI(QDialog *parent)
{
parent->setWindowTitle(i18nc("@title:window", "Advanced Settings"));
const auto mainLayout = new QVBoxLayout{parent};
const auto scrollArea = new ScrollArea{parent};
{
scrollArea->setFocusPolicy(Qt::NoFocus);
scrollArea->setFrameStyle(QFrame::NoFrame);
scrollArea->setBackgroundRole(parent->backgroundRole());
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
const auto scrollLayout = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
{
const auto groupBox = new QGroupBox{i18nc("@title:group", "Key Material"), scrollArea};
const auto formLayout = new QFormLayout(groupBox);
keyAlgoCB = new QComboBox(groupBox);
formLayout->addRow(i18n("Algorithm:"), keyAlgoCB);
scrollLayout->addWidget(groupBox);
}
{
const auto groupBox = new QGroupBox{i18nc("@title:group", "Certificate Usage"), scrollArea};
const auto usageLayout = new QVBoxLayout;
signingCB = new QRadioButton{i18nc("@option:check", "Signing"), groupBox};
usageLayout->addWidget(signingCB);
encryptionCB = new QRadioButton{i18nc("@option:check", "Encryption"), groupBox};
encryptionCB->setChecked(true);
usageLayout->addWidget(encryptionCB);
authenticationCB = new QRadioButton{i18nc("@option:check", "Authentication"), groupBox};
usageLayout->addWidget(authenticationCB);
{
const auto hbox = new QHBoxLayout;
expiryCB = new QCheckBox{i18nc("@option:check", "Valid until:"), groupBox};
expiryCB->setChecked(true);
hbox->addWidget(expiryCB);
expiryDE = new KDateComboBox(groupBox);
hbox->addWidget(expiryDE, 1);
connect(expiryCB, &QCheckBox::toggled, expiryDE, &KDateComboBox::setEnabled);
usageLayout->addLayout(hbox);
}
primaryKeyExpiration = new QLabel(groupBox);
primaryKeyExpiration->setVisible(false);
usageLayout->addWidget(primaryKeyExpiration);
groupBox->setLayout(usageLayout);
scrollLayout->addWidget(groupBox);
}
scrollLayout->addStretch(1);
}
mainLayout->addWidget(scrollArea);
buttonBox = new QDialogButtonBox{parent};
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
mainLayout->addWidget(buttonBox);
}
} ui;
};
AddSubkeyDialog::AddSubkeyDialog(const GpgME::Key &parent, QWidget *p)
: QDialog{p}
, d{new Private{this}}
{
setWindowTitle(i18nc("@title:window", "Add Subkey"));
d->ui.expiryCB->setEnabled(unlimitedValidityIsAllowed());
if (!parent.subkey(0).neverExpires()) {
d->ui.expiryDE->setMaximumDate(Kleo::Formatting::expirationDate(parent));
d->ui.primaryKeyExpiration->setText(i18n("Expiration of primary key: %1", Kleo::Formatting::expirationDateString(parent)));
d->ui.primaryKeyExpiration->setVisible(true);
}
d->ui.expiryDE->setMinimumDate(QDate::currentDate());
loadDefaults();
connect(d->ui.buttonBox, &QDialogButtonBox::accepted, this, &AddSubkeyDialog::accept);
connect(d->ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
AddSubkeyDialog::~AddSubkeyDialog() = default;
bool AddSubkeyDialog::unlimitedValidityIsAllowed() const
{
- return !Kleo::maximumExpirationDate().isValid();
+ return !Expiration::maximumExpirationDate().isValid();
}
void AddSubkeyDialog::setKeyType(const QString &algorithm)
{
const auto index = d->ui.keyAlgoCB->findData(algorithm);
if (index != -1) {
d->ui.keyAlgoCB->setCurrentIndex(index);
}
}
void AddSubkeyDialog::loadDefaults()
{
- setExpiryDate(defaultExpirationDate(unlimitedValidityIsAllowed() ? ExpirationOnUnlimitedValidity::NoExpiration
- : ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
+ setExpiryDate(defaultExpirationDate(unlimitedValidityIsAllowed() ? Expiration::ExpirationOnUnlimitedValidity::NoExpiration
+ : Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
loadAlgorithms();
loadDefaultKeyType();
}
void AddSubkeyDialog::replaceEntry(const QString &before, const QString &after)
{
const auto currentIndex = d->ui.keyAlgoCB->currentIndex();
const auto index = d->ui.keyAlgoCB->findData(before);
if (index != -1) {
d->ui.keyAlgoCB->removeItem(index);
d->ui.keyAlgoCB->insertItem(index, after, after);
d->ui.keyAlgoCB->setCurrentIndex(currentIndex);
}
}
void AddSubkeyDialog::loadDefaultKeyType()
{
if (DeVSCompliance::isActive()) {
for (const auto &algorithm : DeVSCompliance::preferredCompliantAlgorithms()) {
if (d->ui.keyAlgoCB->findData(QString::fromStdString(algorithm)) != -1) {
setKeyType(QString::fromStdString(algorithm));
break;
}
}
return;
}
}
QDate AddSubkeyDialog::forceDateIntoAllowedRange(QDate date) const
{
const auto minDate = d->ui.expiryDE->minimumDate();
if (minDate.isValid() && date < minDate) {
date = minDate;
}
const auto maxDate = d->ui.expiryDE->maximumDate();
if (maxDate.isValid() && date > maxDate) {
date = maxDate;
}
return date;
}
void AddSubkeyDialog::setExpiryDate(QDate date)
{
if (date.isValid()) {
d->ui.expiryDE->setDate(forceDateIntoAllowedRange(date));
} else {
if (unlimitedValidityIsAllowed()) {
d->ui.expiryDE->setDate(date);
}
}
if (d->ui.expiryCB->isEnabled()) {
d->ui.expiryCB->setChecked(d->ui.expiryDE->isValid());
}
}
KeyUsage AddSubkeyDialog::usage() const
{
if (d->ui.signingCB->isChecked()) {
return KeyUsage(KeyUsage::Sign);
}
if (d->ui.encryptionCB->isChecked()) {
return KeyUsage(KeyUsage::Encrypt);
}
return KeyUsage(KeyUsage::Authenticate);
}
QString AddSubkeyDialog::algo() const
{
return d->ui.keyAlgoCB->currentData().toString();
}
QDate AddSubkeyDialog::expires() const
{
return d->ui.expiryCB->isChecked() ? d->ui.expiryDE->date() : QDate();
}
void AddSubkeyDialog::loadAlgorithms()
{
if (!DeVSCompliance::isActive()) {
d->ui.keyAlgoCB->addItem(i18nc("Default Algorithm", "Default"), QLatin1String("default"));
}
for (const auto &algorithm : DeVSCompliance::isActive() ? DeVSCompliance::compliantAlgorithms() : availableAlgorithms()) {
d->ui.keyAlgoCB->addItem(Formatting::prettyAlgorithmName(algorithm), QString::fromStdString(algorithm));
}
d->ui.keyAlgoCB->setCurrentIndex(0);
}
#include "moc_addsubkeydialog.cpp"
diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp
index c0ca5138b..d45bc0c56 100644
--- a/src/dialogs/adduseriddialog.cpp
+++ b/src/dialogs/adduseriddialog.cpp
@@ -1,192 +1,192 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/adduseriddialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "adduseriddialog.h"
-#include "nameandemailwidget.h"
-
#include "utils/accessibility.h"
#include "utils/scrollarea.h"
#include "view/htmllabel.h"
+#include <Libkleo/NameAndEmailWidget>
+
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <KSharedConfig>
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
class AddUserIDDialog::Private
{
friend class ::Kleo::AddUserIDDialog;
AddUserIDDialog *const q;
struct {
ScrollArea *scrollArea;
NameAndEmailWidget *nameAndEmail;
HtmlLabel *resultLabel;
QDialogButtonBox *buttonBox;
} ui;
LabelHelper labelHelper;
public:
explicit Private(AddUserIDDialog *qq)
: q{qq}
{
q->setWindowTitle(i18nc("title:window", "Add User ID"));
const KConfigGroup config{KSharedConfig::openConfig(), QLatin1StringView("CertificateCreationWizard")};
const auto attrOrder = config.readEntry("OpenPGPAttributeOrder", QStringList{});
const auto nameIsRequired = attrOrder.contains(QLatin1StringView{"NAME!"}, Qt::CaseInsensitive);
const auto emailIsRequired = attrOrder.contains(QLatin1StringView{"EMAIL!"}, Qt::CaseInsensitive);
auto mainLayout = new QVBoxLayout{q};
{
const auto infoText = nameIsRequired || emailIsRequired //
? i18n("Enter a name and an email address to use for the user ID.")
: i18n("Enter a name and/or an email address to use for the user ID.");
auto label = new QLabel{infoText, q};
label->setWordWrap(true);
mainLayout->addWidget(label);
}
mainLayout->addWidget(new KSeparator{Qt::Horizontal, q});
ui.scrollArea = new ScrollArea{q};
ui.scrollArea->setFocusPolicy(Qt::NoFocus);
ui.scrollArea->setFrameStyle(QFrame::NoFrame);
ui.scrollArea->setBackgroundRole(q->backgroundRole());
ui.scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui.scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
auto scrollAreaLayout = qobject_cast<QBoxLayout *>(ui.scrollArea->widget()->layout());
scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
ui.nameAndEmail = new NameAndEmailWidget{q};
ui.nameAndEmail->layout()->setContentsMargins(0, 0, 0, 0);
ui.nameAndEmail->setNameIsRequired(nameIsRequired);
ui.nameAndEmail->setNameLabel(config.readEntry("NAME_label"));
ui.nameAndEmail->setNameHint(config.readEntry("NAME_hint", config.readEntry("NAME_placeholder")));
ui.nameAndEmail->setNamePattern(config.readEntry("NAME_regex"));
ui.nameAndEmail->setEmailIsRequired(emailIsRequired);
ui.nameAndEmail->setEmailLabel(config.readEntry("EMAIL_label"));
ui.nameAndEmail->setEmailHint(config.readEntry("EMAIL_hint", config.readEntry("EMAIL_placeholder")));
ui.nameAndEmail->setEmailPattern(config.readEntry("EMAIL_regex"));
scrollAreaLayout->addWidget(ui.nameAndEmail);
scrollAreaLayout->addWidget(new KSeparator{Qt::Horizontal, q});
{
ui.resultLabel = new HtmlLabel{q};
ui.resultLabel->setWordWrap(true);
ui.resultLabel->setFocusPolicy(Qt::ClickFocus);
labelHelper.addLabel(ui.resultLabel);
scrollAreaLayout->addWidget(ui.resultLabel);
}
scrollAreaLayout->addStretch(1);
mainLayout->addWidget(ui.scrollArea);
mainLayout->addWidget(new KSeparator{Qt::Horizontal, q});
ui.buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, q};
mainLayout->addWidget(ui.buttonBox);
connect(ui.nameAndEmail, &NameAndEmailWidget::userIDChanged, q, [this]() {
updateResultLabel();
});
connect(ui.buttonBox, &QDialogButtonBox::accepted, q, [this]() {
checkAccept();
});
connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
updateResultLabel();
}
private:
void checkAccept()
{
QStringList errors;
if (ui.nameAndEmail->userID().isEmpty() && !ui.nameAndEmail->nameIsRequired() && !ui.nameAndEmail->emailIsRequired()) {
errors.push_back(i18n("Enter a name or an email address."));
}
const auto nameError = ui.nameAndEmail->nameError();
if (!nameError.isEmpty()) {
errors.push_back(nameError);
}
const auto emailError = ui.nameAndEmail->emailError();
if (!emailError.isEmpty()) {
errors.push_back(emailError);
}
if (errors.size() > 1) {
KMessageBox::errorList(q, i18n("There is a problem."), errors);
} else if (!errors.empty()) {
KMessageBox::error(q, errors.first());
} else {
q->accept();
}
}
void updateResultLabel()
{
ui.resultLabel->setHtml(i18nc("@info",
"<div>This is how the new user ID will be stored in the certificate:</div>"
"<center><strong>%1</strong></center>",
ui.nameAndEmail->userID().toHtmlEscaped()));
}
};
AddUserIDDialog::AddUserIDDialog(QWidget *parent, Qt::WindowFlags f)
: QDialog{parent, f}
, d(new Private{this})
{
}
AddUserIDDialog::~AddUserIDDialog() = default;
void AddUserIDDialog::setName(const QString &name)
{
d->ui.nameAndEmail->setName(name);
}
QString AddUserIDDialog::name() const
{
return d->ui.nameAndEmail->name();
}
void AddUserIDDialog::setEmail(const QString &email)
{
d->ui.nameAndEmail->setEmail(email);
}
QString AddUserIDDialog::email() const
{
return d->ui.nameAndEmail->email();
}
QString AddUserIDDialog::userID() const
{
return d->ui.nameAndEmail->userID();
}
#include "moc_adduseriddialog.cpp"
diff --git a/src/dialogs/certificatedetailsinputwidget.cpp b/src/dialogs/certificatedetailsinputwidget.cpp
index 786b03ce0..dd571af41 100644
--- a/src/dialogs/certificatedetailsinputwidget.cpp
+++ b/src/dialogs/certificatedetailsinputwidget.cpp
@@ -1,363 +1,363 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/certificatedetailsinputwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatedetailsinputwidget.h"
#include <utils/userinfo.h>
-#include <utils/validation.h>
#include <Libkleo/Dn>
#include <Libkleo/OidMap>
#include <Libkleo/Stl_Util>
+#include <Libkleo/Validation>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QValidator>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
namespace
{
struct Line {
QString attr;
QString label;
QString regex;
QLineEdit *edit;
std::shared_ptr<QValidator> validator;
bool required;
};
QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
QString attributeLabel(const QString &attr)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = DN::attributeNameToLabel(attr);
if (!label.isEmpty()) {
return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr);
} else {
return attr;
}
}
QLineEdit *addRow(QGridLayout *l, const QString &label, const QString &preset, const std::shared_ptr<QValidator> &validator, bool readonly, bool required)
{
Q_ASSERT(l);
auto lb = new QLabel(l->parentWidget());
lb->setText(i18nc("interpunctation for labels", "%1:", label));
auto le = new QLineEdit(l->parentWidget());
le->setText(preset);
if (validator) {
le->setValidator(validator.get());
}
le->setReadOnly(readonly && le->hasAcceptableInput());
auto reqLB = new QLabel(l->parentWidget());
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
const int row = l->rowCount();
l->addWidget(lb, row, 0);
l->addWidget(le, row, 1);
l->addWidget(reqLB, row, 2);
return le;
}
bool hasIntermediateInput(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return v && v->validate(text, pos) == QValidator::Intermediate;
}
QString requirementsAreMet(const QList<Line> &lines)
{
for (const Line &line : lines) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << line.attr << "\" against \"" << le->text() << "\":";
if (le->text().trimmed().isEmpty()) {
if (line.required) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else {
return xi18nc("@info",
"<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
}
}
} else if (hasIntermediateInput(le)) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else {
return xi18nc("@info",
"<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
}
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else {
return xi18nc("@info",
"<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
}
}
}
return QString();
}
}
class CertificateDetailsInputWidget::Private
{
friend class ::Kleo::Dialogs::CertificateDetailsInputWidget;
CertificateDetailsInputWidget *const q;
struct {
QGridLayout *gridLayout;
QList<Line> lines;
QLineEdit *dn;
QLabel *error;
} ui;
public:
Private(CertificateDetailsInputWidget *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
ui.gridLayout = new QGridLayout();
mainLayout->addLayout(ui.gridLayout);
createForm();
mainLayout->addStretch(1);
ui.dn = new QLineEdit();
ui.dn->setFrame(false);
ui.dn->setAlignment(Qt::AlignCenter);
ui.dn->setReadOnly(true);
mainLayout->addWidget(ui.dn);
ui.error = new QLabel();
{
QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(ui.error->sizePolicy().hasHeightForWidth());
ui.error->setSizePolicy(sizePolicy);
}
{
QPalette palette;
QBrush brush(QColor(255, 0, 0, 255));
brush.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Active, QPalette::WindowText, brush);
palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush);
QBrush brush1(QColor(114, 114, 114, 255));
brush1.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1);
ui.error->setPalette(palette);
}
ui.error->setTextFormat(Qt::RichText);
// set error label to have a fixed height of two lines:
ui.error->setText(QStringLiteral("2<br>1"));
ui.error->setFixedHeight(ui.error->minimumSizeHint().height());
ui.error->clear();
mainLayout->addWidget(ui.error);
// select the preset text in the first line edit
if (!ui.lines.empty()) {
ui.lines.first().edit->selectAll();
}
// explicitly update DN and check requirements after setup is complete
updateDN();
checkRequirements();
}
~Private()
{
// remember current attribute values as presets for next certificate
KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
for (const Line &line : ui.lines) {
const QString attr = attributeFromKey(line.attr);
const QString value = line.edit->text().trimmed();
config.writeEntry(attr, value);
}
config.sync();
}
void createForm()
{
static const QStringList defaultAttributeOrder = {
QStringLiteral("CN!"),
QStringLiteral("EMAIL!"),
QStringLiteral("L"),
QStringLiteral("OU"),
QStringLiteral("O"),
QStringLiteral("C"),
};
const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
const QStringList attrOrder = config.readEntry("DNAttributeOrder", defaultAttributeOrder);
for (const QString &rawKey : attrOrder) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString defaultPreset = //
(attr == QLatin1StringView("CN")) ? userFullName()
: (attr == QLatin1StringView("EMAIL")) ? userEmailAddress()
: QString();
const QString preset = config.readEntry(attr, defaultPreset);
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1StringView("_label"), attributeLabel(attr));
const QString regex = config.readEntry(attr + QLatin1StringView("_regex"));
std::shared_ptr<QValidator> validator;
if (attr == QLatin1StringView("EMAIL")) {
validator = regex.isEmpty() ? Validation::email() : Validation::email(regex);
} else if (!regex.isEmpty()) {
validator = std::make_shared<QRegularExpressionValidator>(QRegularExpression{regex});
}
QLineEdit *le = addRow(ui.gridLayout, label, preset, validator, readonly, required);
const Line line = {attr, label, regex, le, validator, required};
ui.lines.push_back(line);
if (attr != QLatin1StringView("EMAIL")) {
connect(le, &QLineEdit::textChanged, le, [this]() {
updateDN();
});
}
connect(le, &QLineEdit::textChanged, le, [this]() {
checkRequirements();
});
}
}
void updateDN()
{
ui.dn->setText(cmsDN());
}
QString cmsDN() const
{
DN dn;
for (const Line &line : ui.lines) {
const QString text = line.edit->text().trimmed();
if (text.isEmpty()) {
continue;
}
QString attr = attributeFromKey(line.attr);
if (attr == QLatin1StringView("EMAIL")) {
continue;
}
if (const char *const oid = oidForAttributeName(attr)) {
attr = QString::fromUtf8(oid);
}
dn.append(DN::Attribute(attr, text));
}
return dn.dn();
}
void checkRequirements()
{
const QString error = requirementsAreMet(ui.lines);
ui.error->setText(error);
Q_EMIT q->validityChanged(error.isEmpty());
}
QLineEdit *attributeWidget(const QString &attribute)
{
for (const Line &line : ui.lines) {
if (attributeFromKey(line.attr) == attribute) {
return line.edit;
}
}
qCWarning(KLEOPATRA_LOG) << "CertificateDetailsInputWidget: No widget for attribute" << attribute;
return nullptr;
}
void setAttributeValue(const QString &attribute, const QString &value)
{
QLineEdit *w = attributeWidget(attribute);
if (w) {
w->setText(value);
}
}
QString attributeValue(const QString &attribute)
{
const QLineEdit *w = attributeWidget(attribute);
return w ? w->text().trimmed() : QString();
}
};
CertificateDetailsInputWidget::CertificateDetailsInputWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
CertificateDetailsInputWidget::~CertificateDetailsInputWidget()
{
}
void CertificateDetailsInputWidget::setName(const QString &name)
{
d->setAttributeValue(QStringLiteral("CN"), name);
}
void CertificateDetailsInputWidget::setEmail(const QString &email)
{
d->setAttributeValue(QStringLiteral("EMAIL"), email);
}
QString CertificateDetailsInputWidget::email() const
{
return d->attributeValue(QStringLiteral("EMAIL"));
}
QString CertificateDetailsInputWidget::dn() const
{
return d->ui.dn->text();
}
#include "moc_certificatedetailsinputwidget.cpp"
diff --git a/src/dialogs/certifywidget.cpp b/src/dialogs/certifywidget.cpp
index c8320abe0..0faaa72c0 100644
--- a/src/dialogs/certifywidget.cpp
+++ b/src/dialogs/certifywidget.cpp
@@ -1,959 +1,959 @@
/* dialogs/certifywidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2019, 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certifywidget.h"
#include "dialogs/animatedexpander.h"
#include "view/infofield.h"
#include <utils/accessibility.h>
-#include <utils/expiration.h>
#include <utils/gui-helper.h>
#include <settings.h>
#include "kleopatra_debug.h"
#include <KConfigGroup>
#include <KDateComboBox>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSeparator>
#include <KSharedConfig>
#include <Libkleo/Algorithm>
#include <Libkleo/DefaultKeyFilter>
+#include <Libkleo/Expiration>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/KeySelectionCombo>
#include <Libkleo/Predicates>
#include <Libkleo/TreeWidget>
#include <QGpgME/ChangeOwnerTrustJob>
#include <QGpgME/Protocol>
#include <QAction>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QLineEdit>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QScrollArea>
#include <QToolButton>
#include <QVBoxLayout>
#include <gpgme++/key.h>
Q_DECLARE_METATYPE(GpgME::UserID)
using namespace Kleo;
using namespace GpgME;
static QDebug operator<<(QDebug s, const GpgME::UserID &userID)
{
return s << Formatting::prettyUserID(userID);
}
namespace
{
class SecKeyFilter : public DefaultKeyFilter
{
public:
SecKeyFilter()
: DefaultKeyFilter()
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
setCanCertify(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::Set);
}
bool matches(const GpgME::Key &key, Kleo::KeyFilter::MatchContexts contexts) const override
{
if (!(availableMatchContexts() & contexts)) {
return false;
}
if (_detail::ByFingerprint<std::equal_to>()(key, mExcludedKey)) {
return false;
}
return DefaultKeyFilter::matches(key, contexts);
}
void setExcludedKey(const GpgME::Key &key)
{
mExcludedKey = key;
}
private:
GpgME::Key mExcludedKey;
};
auto checkBoxSize(const QCheckBox *checkBox)
{
QStyleOptionButton opt;
return checkBox->style()->sizeFromContents(QStyle::CT_CheckBox, &opt, QSize(), checkBox);
}
class TreeWidgetInternal : public TreeWidget
{
Q_OBJECT
public:
using TreeWidget::TreeWidget;
protected:
bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) override
{
if (event && event->type() == QEvent::KeyPress) {
const auto *const keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_Select) {
// toggle checked state regardless of the index's column
return TreeWidget::edit(index.siblingAtColumn(0), trigger, event);
}
}
return TreeWidget::edit(index, trigger, event);
}
};
struct UserIDCheckState {
GpgME::UserID userId;
Qt::CheckState checkState;
};
}
class CertifyWidget::Private
{
public:
enum Role { UserIdRole = Qt::UserRole };
enum Mode {
SingleCertification,
BulkCertification,
};
enum TagsState {
TagsMustBeChecked,
TagsLoading,
TagsLoaded,
};
Private(CertifyWidget *qq)
: q{qq}
{
auto mainLay = new QVBoxLayout{q};
{
mInfoLabel = new QLabel{i18n("Verify the fingerprint, mark the user IDs you want to certify, "
"and select the key you want to certify the user IDs with.<br>"
"<i>Note: Only the fingerprint clearly identifies the key and its owner.</i>"),
q};
mInfoLabel->setWordWrap(true);
labelHelper.addLabel(mInfoLabel);
mainLay->addWidget(mInfoLabel);
}
mainLay->addWidget(new KSeparator{Qt::Horizontal, q});
{
auto grid = new QGridLayout;
grid->setColumnStretch(1, 1);
int row = -1;
row++;
mFprField = std::make_unique<InfoField>(i18n("Fingerprint:"), q);
grid->addWidget(mFprField->label(), row, 0);
grid->addLayout(mFprField->layout(), row, 1);
row++;
auto label = new QLabel{i18n("Certify with:"), q};
mSecKeySelect = new KeySelectionCombo{/* secretOnly= */ true, q};
mSecKeySelect->setKeyFilter(std::make_shared<SecKeyFilter>());
label->setBuddy(mSecKeySelect);
grid->addWidget(label, row, 0);
grid->addWidget(mSecKeySelect);
mainLay->addLayout(grid);
}
mMissingOwnerTrustInfo = new KMessageWidget{q};
mSetOwnerTrustAction = new QAction{q};
mSetOwnerTrustAction->setText(i18nc("@action:button", "Set Owner Trust"));
mSetOwnerTrustAction->setToolTip(i18nc("@info:tooltip",
"Click to set the trust level of the selected certification key to ultimate trust. "
"This is what you usually want to do for your own keys."));
connect(mSetOwnerTrustAction, &QAction::triggered, q, [this]() {
setOwnerTrust();
});
mMissingOwnerTrustInfo->addAction(mSetOwnerTrustAction);
mMissingOwnerTrustInfo->setVisible(false);
mainLay->addWidget(mMissingOwnerTrustInfo);
mainLay->addWidget(new KSeparator{Qt::Horizontal, q});
mBadCertificatesInfo = new KMessageWidget{q};
mBadCertificatesInfo->setMessageType(KMessageWidget::Warning);
mBadCertificatesInfo->setIcon(QIcon::fromTheme(QStringLiteral("data-warning"), QIcon::fromTheme(QStringLiteral("dialog-warning"))));
mBadCertificatesInfo->setText(i18nc("@info", "One or more certificates cannot be certified."));
mBadCertificatesInfo->setCloseButtonVisible(false);
mBadCertificatesInfo->setVisible(false);
mainLay->addWidget(mBadCertificatesInfo);
userIdListView = new TreeWidget{q};
userIdListView->setAccessibleName(i18n("User IDs"));
userIdListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
userIdListView->setSelectionMode(QAbstractItemView::SingleSelection);
userIdListView->setRootIsDecorated(false);
userIdListView->setUniformRowHeights(true);
userIdListView->setAllColumnsShowFocus(false);
userIdListView->setHeaderHidden(true);
userIdListView->setHeaderLabels({i18nc("@title:column", "User ID")});
mainLay->addWidget(userIdListView, 1);
// Setup the advanced area
mAdvancedOptionsExpander = new AnimatedExpander{i18n("Advanced"), i18n("Show advanced options"), q};
mainLay->addWidget(mAdvancedOptionsExpander);
auto advLay = new QVBoxLayout;
mExportCB = new QCheckBox{q};
mExportCB->setText(i18n("Certify for everyone to see (exportable)"));
mExportCB->setToolTip(xi18nc("@info:tooltip",
"Check this option, if you want to share your certifications with others. "
"If you just want to mark certificates as certified for yourself, then you can uncheck it."));
advLay->addWidget(mExportCB);
{
auto layout = new QHBoxLayout;
mPublishCB = new QCheckBox{q};
if (keyserver().startsWith(QLatin1StringView("ldap:")) || keyserver().startsWith(QLatin1StringView("ldaps:"))) {
mPublishCB->setText(i18nc("@label:checkbox", "Publish in internal directory"));
} else {
mPublishCB->setText(i18nc("@label:checkbox", "Publish to %1", keyserver()));
}
mPublishCB->setToolTip(xi18nc("@info:tooltip",
"Check this option if you want to upload your certifications to a certificate "
"directory after successful certification."));
mPublishCB->setEnabled(mExportCB->isChecked() && haveKeyserverConfigured());
mPublishCB->setVisible(haveKeyserverConfigured());
layout->addSpacing(checkBoxSize(mExportCB).width());
layout->addWidget(mPublishCB);
advLay->addLayout(layout);
}
{
auto tagsLay = new QHBoxLayout;
auto label = new QLabel{i18n("Tags:"), q};
mTagsLE = new QLineEdit{q};
label->setBuddy(mTagsLE);
const auto tooltip = i18n("You can use this to add additional info to a certification.") + QStringLiteral("<br/><br/>")
+ i18n("Tags created by anyone with full certification trust "
"are shown in the keylist and can be searched.");
label->setToolTip(tooltip);
mTagsLE->setToolTip(tooltip);
tagsLay->addWidget(label);
tagsLay->addWidget(mTagsLE, 1);
advLay->addLayout(tagsLay);
}
{
auto layout = new QHBoxLayout;
mExpirationCheckBox = new QCheckBox{q};
mExpirationCheckBox->setText(i18n("Expiration:"));
mExpirationDateEdit = new KDateComboBox{q};
- Kleo::setUpExpirationDateComboBox(mExpirationDateEdit, {QDate::currentDate().addDays(1), QDate{}});
- mExpirationDateEdit->setDate(Kleo::defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
+ Expiration::setUpExpirationDateComboBox(mExpirationDateEdit, {QDate::currentDate().addDays(1), QDate{}});
+ mExpirationDateEdit->setDate(Expiration::defaultExpirationDate(Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
mExpirationDateEdit->setEnabled(mExpirationCheckBox->isChecked());
const auto tooltip = i18n("You can use this to set an expiration date for a certification.") + QStringLiteral("<br/><br/>")
+ i18n("By setting an expiration date, you can limit the validity of "
"your certification to a certain amount of time. Once the expiration "
"date has passed, your certification is no longer valid.");
mExpirationCheckBox->setToolTip(tooltip);
mExpirationDateEdit->setToolTip(tooltip);
layout->addWidget(mExpirationCheckBox);
layout->addWidget(mExpirationDateEdit, 1);
advLay->addLayout(layout);
}
{
mTrustSignatureCB = new QCheckBox{q};
mTrustSignatureWidgets.addWidget(mTrustSignatureCB);
mTrustSignatureCB->setText(i18n("Certify as trusted introducer"));
const auto tooltip = i18n("You can use this to certify a trusted introducer for a domain.") + QStringLiteral("<br/><br/>")
+ i18n("All certificates with email addresses belonging to the domain "
"that have been certified by the trusted introducer are treated "
"as certified, i.e. a trusted introducer acts as a kind of "
"intermediate CA for a domain.");
mTrustSignatureCB->setToolTip(tooltip);
advLay->addWidget(mTrustSignatureCB);
}
{
auto layout = new QHBoxLayout;
auto label = new QLabel{i18n("Domain:"), q};
mTrustSignatureWidgets.addWidget(label);
mTrustSignatureDomainLE = new QLineEdit{q};
mTrustSignatureWidgets.addWidget(mTrustSignatureDomainLE);
mTrustSignatureDomainLE->setEnabled(mTrustSignatureCB->isChecked());
label->setBuddy(mTrustSignatureDomainLE);
layout->addSpacing(checkBoxSize(mTrustSignatureCB).width());
layout->addWidget(label);
layout->addWidget(mTrustSignatureDomainLE);
advLay->addLayout(layout);
}
mAdvancedOptionsExpander->setContentLayout(advLay);
connect(userIdListView, &QTreeWidget::itemChanged, q, [this](auto item, auto) {
onItemChanged(item);
});
connect(mExportCB, &QCheckBox::toggled, q, [this](bool on) {
mPublishCB->setEnabled(on && haveKeyserverConfigured());
});
connect(mSecKeySelect, &KeySelectionCombo::currentKeyChanged, q, [this](const GpgME::Key &) {
updateSelectedUserIds();
updateTags();
checkOwnerTrust();
Q_EMIT q->changed();
});
connect(mExpirationCheckBox, &QCheckBox::toggled, q, [this](bool checked) {
mExpirationDateEdit->setEnabled(checked);
Q_EMIT q->changed();
});
connect(mExpirationDateEdit, &KDateComboBox::dateChanged, q, &CertifyWidget::changed);
connect(mTrustSignatureCB, &QCheckBox::toggled, q, [this](bool on) {
mTrustSignatureDomainLE->setEnabled(on);
Q_EMIT q->changed();
});
connect(mTrustSignatureDomainLE, &QLineEdit::textChanged, q, &CertifyWidget::changed);
loadConfig(true);
}
~Private() = default;
void loadConfig(bool loadAll = false)
{
const KConfigGroup conf(KSharedConfig::openConfig(), QStringLiteral("CertifySettings"));
if (loadAll) {
const Settings settings;
mExpirationCheckBox->setChecked(settings.certificationValidityInDays() > 0);
if (settings.certificationValidityInDays() > 0) {
const QDate expirationDate = QDate::currentDate().addDays(settings.certificationValidityInDays());
mExpirationDateEdit->setDate(expirationDate > mExpirationDateEdit->maximumDate() //
? mExpirationDateEdit->maximumDate() //
: expirationDate);
}
mSecKeySelect->setDefaultKey(conf.readEntry("LastKey", QString()));
}
switch (mMode) {
case SingleCertification: {
mExportCB->setChecked(conf.readEntry("ExportCheckState", false));
mPublishCB->setChecked(conf.readEntry("PublishCheckState", false) && haveKeyserverConfigured());
mAdvancedOptionsExpander->setExpanded(conf.readEntry("AdvancedOptionsExpanded", false));
break;
}
case BulkCertification: {
mExportCB->setChecked(conf.readEntry("BulkExportCheckState", true));
mPublishCB->setChecked(conf.readEntry("BulkPublishCheckState", false) && haveKeyserverConfigured());
mAdvancedOptionsExpander->setExpanded(conf.readEntry("BulkAdvancedOptionsExpanded", true));
break;
}
}
}
void saveConfig()
{
KConfigGroup conf{KSharedConfig::openConfig(), QLatin1StringView("CertifySettings")};
if (!secKey().isNull()) {
conf.writeEntry("LastKey", secKey().primaryFingerprint());
}
switch (mMode) {
case SingleCertification: {
conf.writeEntry("ExportCheckState", mExportCB->isChecked());
conf.writeEntry("PublishCheckState", mPublishCB->isChecked());
conf.writeEntry("AdvancedOptionsExpanded", mAdvancedOptionsExpander->isExpanded());
break;
}
case BulkCertification: {
conf.writeEntry("BulkExportCheckState", mExportCB->isChecked());
conf.writeEntry("BulkPublishCheckState", mPublishCB->isChecked());
conf.writeEntry("BulkAdvancedOptionsExpanded", mAdvancedOptionsExpander->isExpanded());
break;
}
}
conf.sync();
}
void setMode(Mode mode)
{
mMode = mode;
switch (mMode) {
case SingleCertification:
break;
case BulkCertification: {
mInfoLabel->setText(i18nc("@info",
"Verify the fingerprints, mark the user IDs you want to certify, "
"and select the certificate you want to certify the user IDs with.<br>"
"<i>Note: Only the fingerprints clearly identify the certificate and its owner.</i>"));
mFprField->setVisible(false);
mTrustSignatureWidgets.setVisible(false);
break;
}
}
loadConfig();
}
void setUpUserIdList(const std::vector<GpgME::UserID> &uids = {})
{
userIdListView->clear();
if (mMode == SingleCertification) {
userIdListView->setColumnCount(1);
userIdListView->setHeaderHidden(true);
// set header labels for accessibility tools to overwrite the default "1"
userIdListView->setHeaderLabels({i18nc("@title:column", "User ID")});
for (const auto &uid : uids) {
if (uid.isInvalid() || Kleo::isRevokedOrExpired(uid)) {
// Skip user IDs that cannot really be certified.
continue;
}
auto item = new QTreeWidgetItem;
item->setData(0, UserIdRole, QVariant::fromValue(uid));
item->setData(0, Qt::DisplayRole, Kleo::Formatting::prettyUserID(uid));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setCheckState(0, Qt::Checked);
userIdListView->addTopLevelItem(item);
}
} else {
const QStringList headers = {i18nc("@title:column", "User ID"), i18nc("@title:column", "Fingerprint")};
userIdListView->setColumnCount(headers.count());
userIdListView->setHeaderHidden(false);
userIdListView->setHeaderLabels(headers);
for (const auto &key : mKeys) {
const auto &uid = key.userID(0);
auto item = new QTreeWidgetItem;
item->setData(0, UserIdRole, QVariant::fromValue(uid));
item->setData(0, Qt::DisplayRole, Kleo::Formatting::prettyUserID(uid));
item->setData(1, Qt::DisplayRole, Kleo::Formatting::prettyID(key.primaryFingerprint()));
item->setData(1, Qt::AccessibleTextRole, Kleo::Formatting::accessibleHexID(key.primaryFingerprint()));
if ((key.protocol() != OpenPGP) || uid.isInvalid() || Kleo::isRevokedOrExpired(uid)) {
item->setFlags(Qt::NoItemFlags);
item->setCheckState(0, Qt::Unchecked);
if (key.protocol() == CMS) {
item->setData(0, Qt::ToolTipRole, i18nc("@info:tooltip", "S/MIME certificates cannot be certified."));
item->setData(1, Qt::ToolTipRole, i18nc("@info:tooltip", "S/MIME certificates cannot be certified."));
} else {
item->setData(0, Qt::ToolTipRole, i18nc("@info:tooltip", "Expired or revoked certificates cannot be certified."));
item->setData(1, Qt::ToolTipRole, i18nc("@info:tooltip", "Expired or revoked certificates cannot be certified."));
}
} else {
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setCheckState(0, Qt::Checked);
}
userIdListView->addTopLevelItem(item);
}
userIdListView->sortItems(0, Qt::AscendingOrder);
userIdListView->resizeColumnToContents(0);
userIdListView->resizeColumnToContents(1);
}
}
void updateSelectedUserIds()
{
if (mMode == SingleCertification) {
return;
}
if (userIdListView->topLevelItemCount() == 0) {
return;
}
// restore check state of primary user ID of previous certification key
if (!mCertificationKey.isNull()) {
for (int i = 0, end = userIdListView->topLevelItemCount(); i < end; ++i) {
const auto uidItem = userIdListView->topLevelItem(i);
const auto itemUserId = getUserId(uidItem);
if (userIDBelongsToKey(itemUserId, mCertificationKey)) {
uidItem->setCheckState(0, mCertificationKeyUserIDCheckState);
uidItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
break; // we only show the primary user IDs
}
}
}
mCertificationKey = mSecKeySelect->currentKey();
// save and unset check state of primary user ID of current certification key
if (!mCertificationKey.isNull()) {
for (int i = 0, end = userIdListView->topLevelItemCount(); i < end; ++i) {
const auto uidItem = userIdListView->topLevelItem(i);
const auto itemUserId = getUserId(uidItem);
if (userIDBelongsToKey(itemUserId, mCertificationKey)) {
mCertificationKeyUserIDCheckState = uidItem->checkState(0);
if (mCertificationKeyUserIDCheckState) {
uidItem->setCheckState(0, Qt::Unchecked);
}
uidItem->setFlags(Qt::ItemIsSelectable);
break; // we only show the primary user IDs
}
}
}
}
void updateTags()
{
struct ItemAndRemark {
QTreeWidgetItem *item;
QString remark;
};
if (mTagsState != TagsLoaded) {
return;
}
if (mTagsLE->isModified()) {
return;
}
GpgME::Key remarkKey = mSecKeySelect->currentKey();
if (!remarkKey.isNull()) {
std::vector<ItemAndRemark> itemsAndRemarks;
// first choose the remark we want to prefill the Tags field with
QString remark;
for (int i = 0, end = userIdListView->topLevelItemCount(); i < end; ++i) {
const auto item = userIdListView->topLevelItem(i);
if (item->isDisabled()) {
continue;
}
const auto uid = getUserId(item);
GpgME::Error err;
const char *c_remark = uid.remark(remarkKey, err);
const QString itemRemark = (!err && c_remark) ? QString::fromUtf8(c_remark) : QString{};
if (!itemRemark.isEmpty() && (itemRemark != remark)) {
if (!remark.isEmpty()) {
qCDebug(KLEOPATRA_LOG) << "Different remarks on user IDs. Taking last.";
}
remark = itemRemark;
}
itemsAndRemarks.push_back({item, itemRemark});
}
// then select the user IDs with the chosen remark; this prevents overwriting existing
// different remarks on the other user IDs (as long as the user doesn't select any of
// the unselected user IDs with a different remark)
if (!remark.isEmpty()) {
for (const auto &[item, itemRemark] : itemsAndRemarks) {
item->setCheckState(0, itemRemark == remark ? Qt::Checked : Qt::Unchecked);
}
}
mTagsLE->setText(remark);
}
}
void updateTrustSignatureDomain()
{
if (mMode == SingleCertification) {
if (mTrustSignatureDomainLE->text().isEmpty() && certificate().numUserIDs() == 1) {
// try to guess the domain to use for the trust signature
const auto address = certificate().userID(0).addrSpec();
const auto atPos = address.find('@');
if (atPos != std::string::npos) {
const auto domain = address.substr(atPos + 1);
mTrustSignatureDomainLE->setText(QString::fromUtf8(domain.c_str(), domain.size()));
}
}
}
}
void loadAllTags()
{
const auto keyWithoutTags = std::find_if(mKeys.cbegin(), mKeys.cend(), [](const auto &key) {
return (key.protocol() == GpgME::OpenPGP) && !(key.keyListMode() & GpgME::SignatureNotations);
});
const auto indexOfKeyWithoutTags = std::distance(mKeys.cbegin(), keyWithoutTags);
if (indexOfKeyWithoutTags < signed(mKeys.size())) {
auto loadTags = [this, indexOfKeyWithoutTags]() {
Q_ASSERT(indexOfKeyWithoutTags < signed(mKeys.size()));
// call update() on the reference to the vector element because it swaps key with the updated key
mKeys[indexOfKeyWithoutTags].update();
loadAllTags();
};
QMetaObject::invokeMethod(q, loadTags, Qt::QueuedConnection);
return;
}
mTagsState = TagsLoaded;
QMetaObject::invokeMethod(
q,
[this]() {
setUpWidget();
},
Qt::QueuedConnection);
}
bool ensureTagsLoaded()
{
Q_ASSERT(mTagsState != TagsLoading);
if (mTagsState == TagsLoaded) {
return true;
}
const auto allTagsAreLoaded = std::ranges::all_of(mKeys, [](const auto &key) {
return (key.protocol() != GpgME::OpenPGP) || (key.keyListMode() & GpgME::SignatureNotations);
});
if (allTagsAreLoaded) {
mTagsState = TagsLoaded;
} else {
mTagsState = TagsLoading;
QMetaObject::invokeMethod(
q,
[this]() {
loadAllTags();
},
Qt::QueuedConnection);
}
return mTagsState == TagsLoaded;
}
void setUpWidget()
{
if (!ensureTagsLoaded()) {
return;
}
if (mMode == SingleCertification) {
const auto key = certificate();
mFprField->setValue(QStringLiteral("<b>") + Formatting::prettyID(key.primaryFingerprint()) + QStringLiteral("</b>"),
Formatting::accessibleHexID(key.primaryFingerprint()));
setUpUserIdList(mUserIds.empty() ? key.userIDs() : mUserIds);
auto keyFilter = std::make_shared<SecKeyFilter>();
keyFilter->setExcludedKey(key);
mSecKeySelect->setKeyFilter(keyFilter);
updateTrustSignatureDomain();
} else {
// check for certificates that cannot be certified
const auto haveBadCertificates = std::ranges::any_of(mKeys, [](const auto &key) {
const auto &uid = key.userID(0);
return (key.protocol() != OpenPGP) || uid.isInvalid() || Kleo::isRevokedOrExpired(uid);
});
if (haveBadCertificates) {
mBadCertificatesInfo->animatedShow();
}
setUpUserIdList();
}
updateTags();
updateSelectedUserIds();
Q_EMIT q->changed();
}
GpgME::Key certificate() const
{
Q_ASSERT(mMode == SingleCertification);
return !mKeys.empty() ? mKeys.front() : Key{};
}
void setCertificates(const std::vector<GpgME::Key> &keys, const std::vector<GpgME::UserID> &uids)
{
mKeys = keys;
mUserIds = uids;
mTagsState = TagsMustBeChecked;
setUpWidget();
}
std::vector<GpgME::Key> certificates() const
{
Q_ASSERT(mMode != SingleCertification);
return mKeys;
}
GpgME::Key secKey() const
{
return mSecKeySelect->currentKey();
}
GpgME::UserID getUserId(const QTreeWidgetItem *item) const
{
return item ? item->data(0, UserIdRole).value<UserID>() : UserID{};
}
void selectUserIDs(const std::vector<GpgME::UserID> &uids)
{
for (int i = 0, end = userIdListView->topLevelItemCount(); i < end; ++i) {
const auto uidItem = userIdListView->topLevelItem(i);
const auto itemUserId = getUserId(uidItem);
const bool userIdIsInList = std::ranges::any_of(uids, [itemUserId](const auto &uid) {
return Kleo::userIDsAreEqual(itemUserId, uid);
});
uidItem->setCheckState(0, userIdIsInList ? Qt::Checked : Qt::Unchecked);
}
}
std::vector<GpgME::UserID> selectedUserIDs() const
{
std::vector<GpgME::UserID> userIds;
userIds.reserve(userIdListView->topLevelItemCount());
for (int i = 0, end = userIdListView->topLevelItemCount(); i < end; ++i) {
const auto *const uidItem = userIdListView->topLevelItem(i);
if (uidItem->checkState(0) == Qt::Checked) {
userIds.push_back(getUserId(uidItem));
}
}
qCDebug(KLEOPATRA_LOG) << "Checked user IDs:" << userIds;
return userIds;
}
bool exportableSelected() const
{
return mExportCB->isChecked();
}
bool publishSelected() const
{
return mPublishCB->isChecked();
}
QString tags() const
{
return mTagsLE->text().trimmed();
}
bool isValid() const
{
static const QRegularExpression domainNameRegExp{QStringLiteral(R"(^\s*((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}\s*$)"),
QRegularExpression::CaseInsensitiveOption};
if (mTagsState != TagsLoaded) {
return false;
}
// do not accept null keys
if (mKeys.empty() || mSecKeySelect->currentKey().isNull()) {
return false;
}
// do not accept empty list of user IDs
const auto userIds = selectedUserIDs();
if (userIds.empty()) {
return false;
}
// do not accept if any of the selected user IDs belongs to the certification key
const auto certificationKey = mSecKeySelect->currentKey();
const auto userIdToCertifyBelongsToCertificationKey = std::any_of(userIds.cbegin(), userIds.cend(), [certificationKey](const auto &userId) {
return Kleo::userIDBelongsToKey(userId, certificationKey);
});
if (userIdToCertifyBelongsToCertificationKey) {
return false;
}
if (mExpirationCheckBox->isChecked() && !mExpirationDateEdit->isValid()) {
return false;
}
if (mTrustSignatureCB->isChecked() && !domainNameRegExp.match(mTrustSignatureDomainLE->text()).hasMatch()) {
return false;
}
return true;
}
void checkOwnerTrust()
{
const auto secretKey = secKey();
if (secretKey.ownerTrust() != GpgME::Key::Ultimate) {
mMissingOwnerTrustInfo->setMessageType(KMessageWidget::Information);
mMissingOwnerTrustInfo->setIcon(QIcon::fromTheme(QStringLiteral("question")));
mMissingOwnerTrustInfo->setText(i18n("Is this your own key?"));
mSetOwnerTrustAction->setEnabled(true);
mMissingOwnerTrustInfo->animatedShow();
} else {
mMissingOwnerTrustInfo->animatedHide();
}
}
void setOwnerTrust()
{
mSetOwnerTrustAction->setEnabled(false);
QGpgME::ChangeOwnerTrustJob *const j = QGpgME::openpgp()->changeOwnerTrustJob();
connect(j, &QGpgME::ChangeOwnerTrustJob::result, q, [this](const GpgME::Error &err) {
if (err) {
KMessageBox::error(q,
i18n("<p>Changing the certification trust of the key <b>%1</b> failed:</p><p>%2</p>",
Formatting::formatForComboBox(secKey()),
Formatting::errorAsString(err)),
i18nc("@title:window", "Certification Trust Change Failed"));
}
if (err || err.isCanceled()) {
mSetOwnerTrustAction->setEnabled(true);
} else {
mMissingOwnerTrustInfo->setMessageType(KMessageWidget::Positive);
mMissingOwnerTrustInfo->setIcon(QIcon::fromTheme(QStringLiteral("checkmark")));
mMissingOwnerTrustInfo->setText(i18n("Owner trust set successfully."));
}
});
j->start(secKey(), GpgME::Key::Ultimate);
}
void onItemChanged(QTreeWidgetItem *item)
{
Q_EMIT q->changed();
#ifndef QT_NO_ACCESSIBILITY
if (item) {
// assume that the checked state changed
QAccessible::State st;
st.checked = true;
QAccessibleStateChangeEvent e(userIdListView, st);
e.setChild(userIdListView->indexOfTopLevelItem(item));
QAccessible::updateAccessibility(&e);
}
#endif
}
public:
CertifyWidget *const q;
QLabel *mInfoLabel = nullptr;
std::unique_ptr<InfoField> mFprField;
KeySelectionCombo *mSecKeySelect = nullptr;
KMessageWidget *mMissingOwnerTrustInfo = nullptr;
KMessageWidget *mBadCertificatesInfo = nullptr;
TreeWidget *userIdListView = nullptr;
AnimatedExpander *mAdvancedOptionsExpander = nullptr;
QCheckBox *mExportCB = nullptr;
QCheckBox *mPublishCB = nullptr;
QLineEdit *mTagsLE = nullptr;
BulkStateChanger mTrustSignatureWidgets;
QCheckBox *mTrustSignatureCB = nullptr;
QLineEdit *mTrustSignatureDomainLE = nullptr;
QCheckBox *mExpirationCheckBox = nullptr;
KDateComboBox *mExpirationDateEdit = nullptr;
QAction *mSetOwnerTrustAction = nullptr;
LabelHelper labelHelper;
Mode mMode = SingleCertification;
std::vector<GpgME::Key> mKeys;
std::vector<GpgME::UserID> mUserIds;
TagsState mTagsState = TagsMustBeChecked;
GpgME::Key mCertificationKey;
Qt::CheckState mCertificationKeyUserIDCheckState;
};
CertifyWidget::CertifyWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
Kleo::CertifyWidget::~CertifyWidget() = default;
void CertifyWidget::setCertificate(const GpgME::Key &key, const std::vector<GpgME::UserID> &uids)
{
Q_ASSERT(!key.isNull());
d->setMode(Private::SingleCertification);
d->setCertificates({key}, uids);
}
GpgME::Key CertifyWidget::certificate() const
{
return d->certificate();
}
void CertifyWidget::setCertificates(const std::vector<GpgME::Key> &keys)
{
d->setMode(Private::BulkCertification);
d->setCertificates(keys, {});
}
std::vector<GpgME::Key> CertifyWidget::certificates() const
{
return d->certificates();
}
void CertifyWidget::selectUserIDs(const std::vector<GpgME::UserID> &uids)
{
d->selectUserIDs(uids);
}
std::vector<GpgME::UserID> CertifyWidget::selectedUserIDs() const
{
return d->selectedUserIDs();
}
GpgME::Key CertifyWidget::secKey() const
{
return d->secKey();
}
bool CertifyWidget::exportableSelected() const
{
return d->exportableSelected();
}
QString CertifyWidget::tags() const
{
return d->tags();
}
bool CertifyWidget::publishSelected() const
{
return d->publishSelected();
}
bool CertifyWidget::trustSignatureSelected() const
{
return d->mTrustSignatureCB->isChecked();
}
QString CertifyWidget::trustSignatureDomain() const
{
return d->mTrustSignatureDomainLE->text().trimmed();
}
QDate CertifyWidget::expirationDate() const
{
return d->mExpirationCheckBox->isChecked() ? d->mExpirationDateEdit->date() : QDate{};
}
bool CertifyWidget::isValid() const
{
return d->isValid();
}
void CertifyWidget::saveState() const
{
d->saveConfig();
}
#include "certifywidget.moc"
#include "moc_certifywidget.cpp"
diff --git a/src/dialogs/expirydialog.cpp b/src/dialogs/expirydialog.cpp
index 5d1510703..6373c8719 100644
--- a/src/dialogs/expirydialog.cpp
+++ b/src/dialogs/expirydialog.cpp
@@ -1,245 +1,245 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/expirydialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "expirydialog.h"
#include "utils/accessibility.h"
-#include "utils/expiration.h"
#include "utils/gui-helper.h"
+#include <Libkleo/Expiration>
#include <Libkleo/Formatting>
#include <KDateComboBox>
#include <KLocalizedString>
#include <KMessageBox>
#include <KStandardGuiItem>
#include <QCheckBox>
#include <QDate>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Dialogs;
class ExpiryDialog::Private
{
friend class ::Kleo::Dialogs::ExpiryDialog;
ExpiryDialog *const q;
public:
Private(Mode mode, ExpiryDialog *qq)
: q{qq}
, ui{mode, qq}
{
ui.neverRB->setEnabled(unlimitedValidityAllowed());
ui.onRB->setEnabled(!fixedExpirationDate());
connect(ui.onCB, &KDateComboBox::dateChanged, q, [this]() {
slotOnDateChanged();
});
}
private:
void slotOnDateChanged();
private:
bool unlimitedValidityAllowed() const;
bool fixedExpirationDate() const;
void setInitialFocus();
private:
bool initialFocusWasSet = false;
struct UI {
QRadioButton *neverRB;
QRadioButton *onRB;
KDateComboBox *onCB;
QCheckBox *updateSubkeysCheckBox;
QLabel *primaryKeyExpirationDate;
LabelHelper labelHelper;
explicit UI(Mode mode, Dialogs::ExpiryDialog *qq)
{
auto mainLayout = new QVBoxLayout{qq};
auto mainWidget = new QWidget{qq};
auto vboxLayout = new QVBoxLayout{mainWidget};
vboxLayout->setContentsMargins(0, 0, 0, 0);
{
auto label = new QLabel{qq};
label->setText(mode == Mode::UpdateIndividualSubkey ? i18n("Please select until when the subkey should be valid:")
: i18n("Please select until when the certificate should be valid:"));
vboxLayout->addWidget(label);
}
neverRB = new QRadioButton(i18n("Unlimited validity"), mainWidget);
neverRB->setChecked(false);
vboxLayout->addWidget(neverRB);
{
auto hboxLayout = new QHBoxLayout;
onRB = new QRadioButton{i18n("Valid until:"), mainWidget};
onRB->setChecked(true);
hboxLayout->addWidget(onRB);
onCB = new KDateComboBox{mainWidget};
- setUpExpirationDateComboBox(onCB);
+ Kleo::Expiration::setUpExpirationDateComboBox(onCB);
hboxLayout->addWidget(onCB);
hboxLayout->addStretch(1);
vboxLayout->addLayout(hboxLayout);
}
primaryKeyExpirationDate = new QLabel(qq);
primaryKeyExpirationDate->setVisible(false);
vboxLayout->addWidget(primaryKeyExpirationDate);
labelHelper.addLabel(primaryKeyExpirationDate);
{
updateSubkeysCheckBox = new QCheckBox{i18n("Also update the validity period of the subkeys"), qq};
updateSubkeysCheckBox->setVisible(mode == Mode::UpdateCertificateWithSubkeys);
vboxLayout->addWidget(updateSubkeysCheckBox);
}
vboxLayout->addStretch(1);
mainLayout->addWidget(mainWidget);
auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq};
auto okButton = buttonBox->button(QDialogButtonBox::Ok);
KGuiItem::assign(okButton, KStandardGuiItem::ok());
okButton->setDefault(true);
okButton->setShortcut(static_cast<int>(Qt::CTRL) | static_cast<int>(Qt::Key_Return));
KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
qq->connect(buttonBox, &QDialogButtonBox::accepted, qq, &ExpiryDialog::accept);
qq->connect(buttonBox, &QDialogButtonBox::rejected, qq, &QDialog::reject);
mainLayout->addWidget(buttonBox);
connect(onRB, &QRadioButton::toggled, onCB, &QWidget::setEnabled);
}
} ui;
};
void ExpiryDialog::Private::slotOnDateChanged()
{
ui.onRB->setAccessibleName(i18nc("Valid until DATE", "Valid until %1", Formatting::accessibleDate(ui.onCB->date())));
}
bool Kleo::Dialogs::ExpiryDialog::Private::unlimitedValidityAllowed() const
{
- return !Kleo::maximumExpirationDate().isValid();
+ return !Kleo::Expiration::maximumExpirationDate().isValid();
}
bool Kleo::Dialogs::ExpiryDialog::Private::fixedExpirationDate() const
{
return ui.onCB->minimumDate() == ui.onCB->maximumDate();
}
void ExpiryDialog::Private::setInitialFocus()
{
if (initialFocusWasSet) {
return;
}
// give focus to the checked radio button
(void)focusFirstCheckedButton({ui.neverRB, ui.onRB});
initialFocusWasSet = true;
}
ExpiryDialog::ExpiryDialog(Mode mode, QWidget *p)
: QDialog{p}
, d{new Private{mode, this}}
{
setWindowTitle(i18nc("@title:window", "Change Validity Period"));
}
ExpiryDialog::~ExpiryDialog() = default;
void ExpiryDialog::setDateOfExpiry(const QDate &date)
{
const QDate current = QDate::currentDate();
if (date.isValid()) {
d->ui.onRB->setChecked(true);
if (date <= current) {
- d->ui.onCB->setDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
+ d->ui.onCB->setDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
} else {
d->ui.onCB->setDate(date);
}
} else {
if (d->unlimitedValidityAllowed()) {
d->ui.neverRB->setChecked(true);
} else {
d->ui.onRB->setChecked(true);
}
- d->ui.onCB->setDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
+ d->ui.onCB->setDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
}
}
QDate ExpiryDialog::dateOfExpiry() const
{
return d->ui.onRB->isChecked() ? d->ui.onCB->date() : QDate{};
}
void ExpiryDialog::setUpdateExpirationOfAllSubkeys(bool update)
{
d->ui.updateSubkeysCheckBox->setChecked(update);
}
bool ExpiryDialog::updateExpirationOfAllSubkeys() const
{
return d->ui.updateSubkeysCheckBox->isChecked();
}
void ExpiryDialog::accept()
{
const auto date = dateOfExpiry();
- if (!Kleo::isValidExpirationDate(date)) {
- KMessageBox::error(this, i18nc("@info", "Error: %1", Kleo::validityPeriodHint()));
+ if (!Kleo::Expiration::isValidExpirationDate(date)) {
+ KMessageBox::error(this, i18nc("@info", "Error: %1", Kleo::Expiration::validityPeriodHint()));
return;
}
QDialog::accept();
}
void ExpiryDialog::showEvent(QShowEvent *event)
{
d->setInitialFocus();
QDialog::showEvent(event);
}
void ExpiryDialog::setPrimaryKey(const GpgME::Key &key)
{
if (!key.subkey(0).neverExpires()) {
d->ui.primaryKeyExpirationDate->setText(i18n("Expiration of primary key: %1", Kleo::Formatting::expirationDateString(key)));
d->ui.onCB->setMaximumDate(Kleo::Formatting::expirationDate(key));
d->ui.primaryKeyExpirationDate->setVisible(true);
const auto keyExpiryDate = QDateTime::fromSecsSinceEpoch(quint32(key.subkey(0).expirationTime())).date();
if (dateOfExpiry() > keyExpiryDate) {
setDateOfExpiry(keyExpiryDate);
}
}
}
#include "moc_expirydialog.cpp"
diff --git a/src/dialogs/nameandemailwidget.cpp b/src/dialogs/nameandemailwidget.cpp
deleted file mode 100644
index e8b994b92..000000000
--- a/src/dialogs/nameandemailwidget.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/nameandemailwidget.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "nameandemailwidget.h"
-
-#include "view/errorlabel.h"
-#include "view/formtextinput.h"
-
-#include <utils/validation.h>
-
-#include <KLocalizedString>
-
-#include <QLineEdit>
-#include <QVBoxLayout>
-
-#include "kleopatra_debug.h"
-
-using namespace Kleo;
-
-namespace
-{
-QString buildUserId(const QString &name, const QString &email)
-{
- if (name.isEmpty()) {
- return email;
- } else if (email.isEmpty()) {
- return name;
- } else {
- return QStringLiteral("%1 <%2>").arg(name, email);
- }
-}
-}
-
-class NameAndEmailWidget::Private
-{
- NameAndEmailWidget *const q;
-
-public:
- struct {
- std::unique_ptr<FormTextInput<QLineEdit>> nameInput;
- std::unique_ptr<FormTextInput<QLineEdit>> emailInput;
- } ui;
-
- explicit Private(NameAndEmailWidget *qq)
- : q{qq}
- {
- auto mainLayout = new QVBoxLayout{q};
-
- {
- ui.nameInput = FormTextInput<QLineEdit>::create(q);
- ui.nameInput->setLabelText(i18nc("@label", "Name"));
- ui.nameInput->setValueRequiredErrorMessage(i18n("Enter a name."));
- setNamePattern({});
-
- mainLayout->addWidget(ui.nameInput->label());
- mainLayout->addWidget(ui.nameInput->hintLabel());
- mainLayout->addWidget(ui.nameInput->errorLabel());
- mainLayout->addWidget(ui.nameInput->widget());
- }
- connect(ui.nameInput->widget(), &QLineEdit::textChanged, q, [this]() {
- Q_EMIT q->userIDChanged();
- });
-
- {
- ui.emailInput = FormTextInput<QLineEdit>::create(q);
- ui.emailInput->setLabelText(i18nc("@label", "Email address"));
- ui.emailInput->setValueRequiredErrorMessage(i18n("Enter an email address."));
- setEmailPattern({});
-
- mainLayout->addWidget(ui.emailInput->label());
- mainLayout->addWidget(ui.emailInput->hintLabel());
- mainLayout->addWidget(ui.emailInput->errorLabel());
- mainLayout->addWidget(ui.emailInput->widget());
- }
- connect(ui.emailInput->widget(), &QLineEdit::textChanged, q, [this]() {
- Q_EMIT q->userIDChanged();
- });
- }
-
- void setNamePattern(const QString &regexp)
- {
- if (regexp.isEmpty()) {
- ui.nameInput->setValidator(Validation::simpleName(Validation::Optional));
- ui.nameInput->setInvalidEntryErrorMessage(
- i18n("The name must not include <, >, and @."),
- i18nc("text for screen readers", "The name must not include less-than sign, greater-than sign, and at sign."));
- } else {
- ui.nameInput->setValidator(Validation::simpleName(regexp, Validation::Optional));
- ui.nameInput->setInvalidEntryErrorMessage(i18n("The name must be in the format required by your organization and "
- "it must not include <, >, and @."),
- i18nc("text for screen readers",
- "The name must be in the format required by your organization and "
- "it must not include less-than sign, greater-than sign, and at sign."));
- }
- }
-
- void setEmailPattern(const QString &regexp)
- {
- if (regexp.isEmpty()) {
- ui.emailInput->setValidator(Validation::email(Validation::Optional));
- ui.emailInput->setInvalidEntryErrorMessage(i18n("Enter an email address in the correct format, like name@example.com."));
- } else {
- ui.emailInput->setValidator(Validation::email(regexp, Validation::Optional));
- ui.emailInput->setInvalidEntryErrorMessage(i18n("Enter an email address in the correct format required by your organization."));
- }
- }
-
- QString name() const
- {
- return ui.nameInput->widget()->text().trimmed();
- }
-
- QString email() const
- {
- return ui.emailInput->widget()->text().trimmed();
- }
-};
-
-NameAndEmailWidget::NameAndEmailWidget(QWidget *parent, Qt::WindowFlags f)
- : QWidget{parent, f}
- , d(new Private{this})
-{
-}
-
-NameAndEmailWidget::~NameAndEmailWidget() = default;
-
-void NameAndEmailWidget::setName(const QString &name)
-{
- d->ui.nameInput->widget()->setText(name);
-}
-
-QString NameAndEmailWidget::name() const
-{
- return d->name();
-}
-
-void NameAndEmailWidget::setNameIsRequired(bool required)
-{
- d->ui.nameInput->setIsRequired(required);
-}
-
-bool NameAndEmailWidget::nameIsRequired() const
-{
- return d->ui.nameInput->isRequired();
-}
-
-void NameAndEmailWidget::setNameLabel(const QString &label)
-{
- if (label.isEmpty()) {
- d->ui.nameInput->setLabelText(i18nc("@label", "Name"));
- } else {
- d->ui.nameInput->setLabelText(label);
- }
-}
-
-QString NameAndEmailWidget::nameLabel() const
-{
- return d->ui.nameInput->label()->text();
-}
-
-void NameAndEmailWidget::setNameHint(const QString &hint)
-{
- d->ui.nameInput->setHint(hint);
-}
-
-QString NameAndEmailWidget::nameHint() const
-{
- return d->ui.nameInput->hintLabel()->text();
-}
-
-void NameAndEmailWidget::setNamePattern(const QString &pattern)
-{
- d->setNamePattern(pattern);
-}
-
-QString NameAndEmailWidget::nameError() const
-{
- return d->ui.nameInput->currentError();
-}
-
-void NameAndEmailWidget::setEmail(const QString &email)
-{
- d->ui.emailInput->widget()->setText(email);
-}
-
-QString NameAndEmailWidget::email() const
-{
- return d->email();
-}
-
-void NameAndEmailWidget::setEmailIsRequired(bool required)
-{
- d->ui.emailInput->setIsRequired(required);
-}
-
-bool NameAndEmailWidget::emailIsRequired() const
-{
- return d->ui.emailInput->isRequired();
-}
-
-void NameAndEmailWidget::setEmailLabel(const QString &label)
-{
- if (label.isEmpty()) {
- d->ui.emailInput->setLabelText(i18nc("@label", "Email address"));
- } else {
- d->ui.emailInput->setLabelText(label);
- }
-}
-
-QString NameAndEmailWidget::emailLabel() const
-{
- return d->ui.emailInput->label()->text();
-}
-
-void NameAndEmailWidget::setEmailHint(const QString &hint)
-{
- d->ui.emailInput->setHint(hint);
-}
-
-QString NameAndEmailWidget::emailHint() const
-{
- return d->ui.emailInput->hintLabel()->text();
-}
-
-void NameAndEmailWidget::setEmailPattern(const QString &pattern)
-{
- d->setEmailPattern(pattern);
-}
-
-QString NameAndEmailWidget::emailError() const
-{
- return d->ui.emailInput->currentError();
-}
-
-QString NameAndEmailWidget::userID() const
-{
- return buildUserId(name(), email());
-}
-
-#include "moc_nameandemailwidget.cpp"
diff --git a/src/dialogs/nameandemailwidget.h b/src/dialogs/nameandemailwidget.h
deleted file mode 100644
index bb3ce7b47..000000000
--- a/src/dialogs/nameandemailwidget.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/nameandemailwidget.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <QWidget>
-
-#include <memory>
-
-namespace Kleo
-{
-
-class NameAndEmailWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit NameAndEmailWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
- ~NameAndEmailWidget() override;
-
- void setName(const QString &name);
- QString name() const;
- void setNameIsRequired(bool required);
- bool nameIsRequired() const;
- void setNameLabel(const QString &label);
- QString nameLabel() const;
- void setNameHint(const QString &hint);
- QString nameHint() const;
- void setNamePattern(const QString &pattern);
- QString nameError() const;
-
- void setEmail(const QString &email);
- QString email() const;
- void setEmailIsRequired(bool required);
- bool emailIsRequired() const;
- void setEmailLabel(const QString &label);
- QString emailLabel() const;
- void setEmailHint(const QString &hint);
- QString emailHint() const;
- void setEmailPattern(const QString &pattern);
- QString emailError() const;
-
- /**
- * Returns the user ID built from the entered name and/or email address.
- */
- QString userID() const;
-
-Q_SIGNALS:
- void userIDChanged() const;
-
-private:
- class Private;
- const std::unique_ptr<Private> d;
-};
-
-} // namespace Kleo
diff --git a/src/dialogs/newopenpgpcertificatedetailsdialog.cpp b/src/dialogs/newopenpgpcertificatedetailsdialog.cpp
deleted file mode 100644
index 5aee92de6..000000000
--- a/src/dialogs/newopenpgpcertificatedetailsdialog.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/newopenpgpcertificatedetailsdialog.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "newopenpgpcertificatedetailsdialog.h"
-
-#include "nameandemailwidget.h"
-
-#include "dialogs/animatedexpander.h"
-#include "newcertificatewizard/keyalgo_p.h"
-#include "utils/expiration.h"
-#include "utils/keyparameters.h"
-#include "utils/scrollarea.h"
-
-#include <kdatecombobox.h>
-#include <settings.h>
-
-#include <Libkleo/Compat>
-#include <Libkleo/Compliance>
-#include <Libkleo/Formatting>
-#include <Libkleo/GnuPG>
-#include <Libkleo/KeyUsage>
-
-#include <KConfigGroup>
-#include <KDateComboBox>
-#include <KLocalizedString>
-#include <KMessageBox>
-#include <KSeparator>
-#include <KSharedConfig>
-
-#include <QCheckBox>
-#include <QDialogButtonBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QVBoxLayout>
-
-#include <QGpgME/CryptoConfig>
-#include <QGpgME/Protocol>
-
-#include "kleopatra_debug.h"
-
-using namespace Kleo;
-using namespace Kleo::NewCertificateUi;
-
-static bool unlimitedValidityIsAllowed()
-{
- return !Kleo::maximumExpirationDate().isValid();
-}
-
-class NewOpenPGPCertificateDetailsDialog::Private
-{
- friend class ::Kleo::NewOpenPGPCertificateDetailsDialog;
- NewOpenPGPCertificateDetailsDialog *const q;
-
- struct UI {
- QLabel *infoLabel;
- ScrollArea *scrollArea;
- NameAndEmailWidget *nameAndEmail;
- QCheckBox *withPassCheckBox;
- QDialogButtonBox *buttonBox;
- QCheckBox *expiryCB;
- KDateComboBox *expiryDE;
- QComboBox *keyAlgoCB;
- QLabel *keyAlgoLabel;
- AnimatedExpander *expander;
-
- UI(QWidget *dialog)
- {
- auto mainLayout = new QVBoxLayout{dialog};
-
- infoLabel = new QLabel{dialog};
- infoLabel->setWordWrap(true);
- mainLayout->addWidget(infoLabel);
-
- mainLayout->addWidget(new KSeparator{Qt::Horizontal, dialog});
-
- scrollArea = new ScrollArea{dialog};
- scrollArea->setFocusPolicy(Qt::NoFocus);
- scrollArea->setFrameStyle(QFrame::NoFrame);
- scrollArea->setBackgroundRole(dialog->backgroundRole());
- scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
- auto scrollAreaLayout = qobject_cast<QBoxLayout *>(scrollArea->widget()->layout());
- scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
-
- nameAndEmail = new NameAndEmailWidget{dialog};
- nameAndEmail->layout()->setContentsMargins(0, 0, 0, 0);
- scrollAreaLayout->addWidget(nameAndEmail);
-
- withPassCheckBox = new QCheckBox{i18n("Protect the generated key with a passphrase."), dialog};
- withPassCheckBox->setToolTip(
- i18n("Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation."));
- scrollAreaLayout->addWidget(withPassCheckBox);
-
- expander = new AnimatedExpander(i18n("Advanced options"), {}, dialog);
- scrollAreaLayout->addWidget(expander);
-
- auto advancedLayout = new QVBoxLayout;
- expander->setContentLayout(advancedLayout);
-
- keyAlgoLabel = new QLabel(dialog);
- keyAlgoLabel->setText(i18nc("The algorithm and strength of encryption key", "Key Material"));
- auto font = keyAlgoLabel->font();
- font.setBold(true);
- keyAlgoLabel->setFont(font);
- advancedLayout->addWidget(keyAlgoLabel);
-
- keyAlgoCB = new QComboBox(dialog);
- keyAlgoLabel->setBuddy(keyAlgoCB);
- advancedLayout->addWidget(keyAlgoCB);
-
- {
- auto hbox = new QHBoxLayout;
-
- expiryCB = new QCheckBox{i18nc("@option:check", "Valid until:"), dialog};
- hbox->addWidget(expiryCB);
-
- expiryDE = new KDateComboBox(dialog);
- hbox->addWidget(expiryDE, 1);
-
- advancedLayout->addLayout(hbox);
- }
-
- scrollAreaLayout->addStretch(1);
-
- mainLayout->addWidget(scrollArea);
-
- mainLayout->addWidget(new KSeparator{Qt::Horizontal, dialog});
-
- buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog};
-
- mainLayout->addWidget(buttonBox);
- }
- } ui;
-
-public:
- explicit Private(NewOpenPGPCertificateDetailsDialog *qq)
- : q{qq}
- , ui{qq}
- , technicalParameters{KeyParameters::OpenPGP}
- {
- q->setWindowTitle(i18nc("title:window", "Create OpenPGP Certificate"));
-
- const KConfigGroup config{KSharedConfig::openConfig(), QLatin1StringView("CertificateCreationWizard")};
- const auto attrOrder = config.readEntry("OpenPGPAttributeOrder", QStringList{});
- const auto nameIsRequired = attrOrder.contains(QLatin1StringView{"NAME!"}, Qt::CaseInsensitive);
- const auto emailIsRequired = attrOrder.contains(QLatin1StringView{"EMAIL!"}, Qt::CaseInsensitive);
-
- ui.infoLabel->setText(nameIsRequired || emailIsRequired //
- ? i18n("Enter a name and an email address to use for the certificate.")
- : i18n("Enter a name and/or an email address to use for the certificate."));
-
- ui.nameAndEmail->setNameIsRequired(nameIsRequired);
- ui.nameAndEmail->setNameLabel(config.readEntry("NAME_label"));
- ui.nameAndEmail->setNameHint(config.readEntry("NAME_hint", config.readEntry("NAME_placeholder")));
- ui.nameAndEmail->setNamePattern(config.readEntry("NAME_regex"));
- ui.nameAndEmail->setEmailIsRequired(emailIsRequired);
- ui.nameAndEmail->setEmailLabel(config.readEntry("EMAIL_label"));
- ui.nameAndEmail->setEmailHint(config.readEntry("EMAIL_hint", config.readEntry("EMAIL_placeholder")));
- ui.nameAndEmail->setEmailPattern(config.readEntry("EMAIL_regex"));
-
- Settings settings;
- ui.expander->setVisible(!settings.hideAdvanced());
-
- const auto conf = QGpgME::cryptoConfig();
- const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints");
- if (entry && entry->boolValue()) {
- qCDebug(KLEOPATRA_LOG) << "Disabling passphrase check box because of agent config.";
- ui.withPassCheckBox->setEnabled(false);
- ui.withPassCheckBox->setChecked(true);
- } else {
- ui.withPassCheckBox->setChecked(config.readEntry("WithPassphrase", false));
- ui.withPassCheckBox->setEnabled(!config.isEntryImmutable("WithPassphrase"));
- }
-
- connect(ui.buttonBox, &QDialogButtonBox::accepted, q, [this]() {
- checkAccept();
- });
- connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
-
- for (const auto &algorithm : DeVSCompliance::isActive() ? DeVSCompliance::compliantAlgorithms() : availableAlgorithms()) {
- ui.keyAlgoCB->addItem(QString::fromStdString(algorithm), QString::fromStdString(algorithm));
- }
- auto cryptoConfig = QGpgME::cryptoConfig();
- if (cryptoConfig) {
- auto pubkeyEntry = getCryptoConfigEntry(QGpgME::cryptoConfig(), "gpg", "default_pubkey_algo");
- if (pubkeyEntry) {
- auto algo = pubkeyEntry->stringValue().split(QLatin1Char('/'))[0];
- if (algo == QStringLiteral("ed25519")) {
- algo = QStringLiteral("curve25519");
- } else if (algo == QStringLiteral("ed448")) {
- algo = QStringLiteral("curve448");
- }
- auto index = ui.keyAlgoCB->findData(algo);
- if (index != -1) {
- ui.keyAlgoCB->setCurrentIndex(index);
- } else {
- ui.keyAlgoCB->setCurrentIndex(0);
- }
- } else {
- ui.keyAlgoCB->setCurrentIndex(0);
- }
- } else {
- ui.keyAlgoCB->setCurrentIndex(0);
- }
-
- Kleo::setUpExpirationDateComboBox(ui.expiryDE);
- ui.expiryCB->setEnabled(true);
- setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
- if (unlimitedValidityIsAllowed()) {
- ui.expiryDE->setEnabled(ui.expiryCB->isChecked());
- } else {
- ui.expiryCB->setEnabled(false);
- }
- connect(ui.expiryCB, &QAbstractButton::toggled, q, [this](bool checked) {
- ui.expiryDE->setEnabled(checked);
- if (checked && !ui.expiryDE->isValid()) {
- setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
- }
- updateTechnicalParameters();
- });
- connect(ui.expiryDE, &KDateComboBox::dateChanged, q, [this]() {
- updateTechnicalParameters();
- });
- connect(ui.keyAlgoCB, &QComboBox::currentIndexChanged, q, [this]() {
- updateTechnicalParameters();
- });
- updateTechnicalParameters(); // set key parameters to default values for OpenPGP
- connect(ui.expander, &AnimatedExpander::startExpanding, q, [this]() {
- q->resize(q->sizeHint().width() + 20, q->sizeHint().height() + ui.expander->contentHeight() + 20);
- });
- }
-
-private:
- void updateTechnicalParameters()
- {
- technicalParameters = KeyParameters{KeyParameters::OpenPGP};
- auto keyType = GpgME::Subkey::AlgoUnknown;
- auto subkeyType = GpgME::Subkey::AlgoUnknown;
- auto algoString = ui.keyAlgoCB->currentData().toString();
- if (algoString.startsWith(QStringLiteral("rsa"))) {
- keyType = GpgME::Subkey::AlgoRSA;
- subkeyType = GpgME::Subkey::AlgoRSA;
- const auto strength = algoString.mid(3).toInt();
- technicalParameters.setKeyLength(strength);
- technicalParameters.setSubkeyLength(strength);
- } else if (algoString == QStringLiteral("curve25519") || algoString == QStringLiteral("curve448")) {
- keyType = GpgME::Subkey::AlgoEDDSA;
- subkeyType = GpgME::Subkey::AlgoECDH;
- if (algoString.endsWith(QStringLiteral("25519"))) {
- technicalParameters.setKeyCurve(QStringLiteral("ed25519"));
- technicalParameters.setSubkeyCurve(QStringLiteral("cv25519"));
- } else {
- technicalParameters.setKeyCurve(QStringLiteral("ed448"));
- technicalParameters.setSubkeyCurve(QStringLiteral("cv448"));
- }
- } else {
- keyType = GpgME::Subkey::AlgoECDSA;
- subkeyType = GpgME::Subkey::AlgoECDH;
- technicalParameters.setKeyCurve(algoString);
- technicalParameters.setSubkeyCurve(algoString);
- }
- technicalParameters.setKeyType(keyType);
- technicalParameters.setSubkeyType(subkeyType);
-
- technicalParameters.setKeyUsage(KeyUsage(KeyUsage::Certify | KeyUsage::Sign));
- technicalParameters.setSubkeyUsage(KeyUsage(KeyUsage::Encrypt));
-
- technicalParameters.setExpirationDate(expiryDate());
- // name and email are set later
- }
-
- QDate expiryDate() const
- {
- return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate{};
- }
-
- void setTechnicalParameters(const KeyParameters &parameters)
- {
- int index;
- if (parameters.keyType() == GpgME::Subkey::AlgoRSA_S) {
- index = ui.keyAlgoCB->findData(QStringLiteral("rsa%1").arg(parameters.keyLength()));
- } else {
- index = ui.keyAlgoCB->findData(parameters.keyCurve());
- }
- ui.keyAlgoCB->setCurrentIndex(index);
- setExpiryDate(parameters.expirationDate());
- }
-
- void checkAccept()
- {
- QStringList errors;
- if (ui.nameAndEmail->userID().isEmpty() && !ui.nameAndEmail->nameIsRequired() && !ui.nameAndEmail->emailIsRequired()) {
- errors.push_back(i18n("Enter a name or an email address."));
- }
- const auto nameError = ui.nameAndEmail->nameError();
- if (!nameError.isEmpty()) {
- errors.push_back(nameError);
- }
- const auto emailError = ui.nameAndEmail->emailError();
- if (!emailError.isEmpty()) {
- errors.push_back(emailError);
- }
- if (errors.size() > 1) {
- KMessageBox::errorList(q, i18n("There is a problem."), errors);
- } else if (!errors.empty()) {
- KMessageBox::error(q, errors.first());
- } else {
- q->accept();
- }
- }
-
- QDate forceDateIntoAllowedRange(QDate date) const
- {
- const auto minDate = ui.expiryDE->minimumDate();
- if (minDate.isValid() && date < minDate) {
- date = minDate;
- }
- const auto maxDate = ui.expiryDE->maximumDate();
- if (maxDate.isValid() && date > maxDate) {
- date = maxDate;
- }
- return date;
- }
-
- void setExpiryDate(QDate date)
- {
- if (date.isValid()) {
- ui.expiryDE->setDate(forceDateIntoAllowedRange(date));
- } else {
- // check if unlimited validity is allowed
- if (unlimitedValidityIsAllowed()) {
- ui.expiryDE->setDate(date);
- }
- }
- if (ui.expiryCB->isEnabled()) {
- ui.expiryCB->setChecked(ui.expiryDE->isValid());
- }
- }
-
-private:
- KeyParameters technicalParameters;
-};
-
-NewOpenPGPCertificateDetailsDialog::NewOpenPGPCertificateDetailsDialog(QWidget *parent, Qt::WindowFlags f)
- : QDialog{parent, f}
- , d(new Private{this})
-{
- resize(sizeHint().width() + 20, sizeHint().height() + 20);
-}
-
-NewOpenPGPCertificateDetailsDialog::~NewOpenPGPCertificateDetailsDialog() = default;
-
-void NewOpenPGPCertificateDetailsDialog::setName(const QString &name)
-{
- d->ui.nameAndEmail->setName(name);
-}
-
-QString NewOpenPGPCertificateDetailsDialog::name() const
-{
- return d->ui.nameAndEmail->name();
-}
-
-void NewOpenPGPCertificateDetailsDialog::setEmail(const QString &email)
-{
- d->ui.nameAndEmail->setEmail(email);
-}
-
-QString NewOpenPGPCertificateDetailsDialog::email() const
-{
- return d->ui.nameAndEmail->email();
-}
-
-void Kleo::NewOpenPGPCertificateDetailsDialog::setKeyParameters(const Kleo::KeyParameters &parameters)
-{
- setName(parameters.name());
- const auto emails = parameters.emails();
- if (!emails.empty()) {
- setEmail(emails.front());
- }
- d->setTechnicalParameters(parameters);
-}
-
-KeyParameters NewOpenPGPCertificateDetailsDialog::keyParameters() const
-{
- // set name and email on a copy of the technical parameters
- auto parameters = d->technicalParameters;
- if (!name().isEmpty()) {
- parameters.setName(name());
- }
- if (!email().isEmpty()) {
- parameters.setEmail(email());
- }
- return parameters;
-}
-
-void Kleo::NewOpenPGPCertificateDetailsDialog::setProtectKeyWithPassword(bool protectKey)
-{
- d->ui.withPassCheckBox->setChecked(protectKey);
-}
-
-bool NewOpenPGPCertificateDetailsDialog::protectKeyWithPassword() const
-{
- return d->ui.withPassCheckBox->isChecked();
-}
-
-#include "moc_newopenpgpcertificatedetailsdialog.cpp"
diff --git a/src/dialogs/newopenpgpcertificatedetailsdialog.h b/src/dialogs/newopenpgpcertificatedetailsdialog.h
deleted file mode 100644
index 77d2f2221..000000000
--- a/src/dialogs/newopenpgpcertificatedetailsdialog.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- dialogs/newopenpgpcertificatedetailsdialog.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <QDialog>
-
-#include <memory>
-
-namespace Kleo
-{
-class KeyParameters;
-
-class NewOpenPGPCertificateDetailsDialog : public QDialog
-{
- Q_OBJECT
-public:
- explicit NewOpenPGPCertificateDetailsDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {});
- ~NewOpenPGPCertificateDetailsDialog() override;
-
- void setName(const QString &name);
- QString name() const;
-
- void setEmail(const QString &email);
- QString email() const;
-
- void setKeyParameters(const KeyParameters &parameters);
- KeyParameters keyParameters() const;
-
- void setProtectKeyWithPassword(bool protectKey);
- bool protectKeyWithPassword() const;
-
-private:
- class Private;
- const std::unique_ptr<Private> d;
-};
-
-} // namespace Kleo
diff --git a/src/dialogs/revokekeydialog.cpp b/src/dialogs/revokekeydialog.cpp
index 62cde6fac..8358e3a7a 100644
--- a/src/dialogs/revokekeydialog.cpp
+++ b/src/dialogs/revokekeydialog.cpp
@@ -1,338 +1,338 @@
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/revokekeydialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "revokekeydialog.h"
#include "dialogs/animatedexpander.h"
#include "utils/accessibility.h"
-#include "view/errorlabel.h"
+#include <Libkleo/ErrorLabel>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QApplication>
#include <QButtonGroup>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFocusEvent>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <QRegularExpression>
#include <QTextEdit>
#include <QVBoxLayout>
#include <gpgme++/global.h>
#include <gpgme++/key.h>
#include <kleopatra_debug.h>
using namespace Kleo;
using namespace GpgME;
namespace
{
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
using QTextEdit::QTextEdit;
Q_SIGNALS:
void editingFinished();
protected:
void focusOutEvent(QFocusEvent *event) override
{
Qt::FocusReason reason = event->reason();
if (reason != Qt::PopupFocusReason || !(QApplication::activePopupWidget() && QApplication::activePopupWidget()->parentWidget() == this)) {
Q_EMIT editingFinished();
}
QTextEdit::focusOutEvent(event);
}
QSize minimumSizeHint() const override
{
return {0, fontMetrics().height() * 3};
}
QSize sizeHint() const override
{
return {0, fontMetrics().height() * 3};
}
};
}
class RevokeKeyDialog::Private
{
friend class ::Kleo::RevokeKeyDialog;
RevokeKeyDialog *const q;
struct {
QLabel *infoLabel = nullptr;
QLabel *descriptionLabel = nullptr;
TextEdit *description = nullptr;
ErrorLabel *descriptionError = nullptr;
QDialogButtonBox *buttonBox = nullptr;
QCheckBox *keyserverCheckbox = nullptr;
} ui;
Key key;
QButtonGroup reasonGroup;
bool descriptionEditingInProgress = false;
QString descriptionAccessibleName;
public:
Private(RevokeKeyDialog *qq)
: q(qq)
{
q->setWindowTitle(i18nc("title:window", "Revoke Certificate"));
auto mainLayout = new QVBoxLayout{q};
ui.infoLabel = new QLabel{q};
mainLayout->addWidget(ui.infoLabel);
reasonGroup.addButton(new QRadioButton{i18nc("@option:radio", "Certificate has been compromised"), q}, static_cast<int>(RevocationReason::Compromised));
reasonGroup.addButton(new QRadioButton{i18nc("@option:radio", "Certificate is superseded"), q}, static_cast<int>(RevocationReason::Superseded));
reasonGroup.addButton(new QRadioButton{i18nc("@option:radio", "Certificate is no longer used"), q}, static_cast<int>(RevocationReason::NoLongerUsed));
reasonGroup.addButton(new QRadioButton{i18nc("@option:radio", "For a different reason"), q}, static_cast<int>(RevocationReason::Unspecified));
reasonGroup.button(static_cast<int>(RevocationReason::Unspecified))->setChecked(true);
auto reasonLayout = new QVBoxLayout;
reasonLayout->setContentsMargins({});
auto expander = new AnimatedExpander(i18nc("@title", "Reason for Revocation (optional)"));
connect(expander, &AnimatedExpander::startExpanding, q, [this, expander]() {
q->resize(q->size().width(), std::max(q->sizeHint().height() + expander->contentHeight() + 20, q->size().height()));
});
expander->setContentLayout(reasonLayout);
ui.keyserverCheckbox = new QCheckBox({});
if (!haveKeyserverConfigured()) {
ui.keyserverCheckbox->setVisible(false);
} else if (keyserver().startsWith(QStringLiteral("ldap://")) || keyserver().startsWith(QStringLiteral("ldaps://"))) {
ui.keyserverCheckbox->setText(i18nc("@option:check", "Upload revoked certificate to internal directory"));
} else {
ui.keyserverCheckbox->setText(i18nc("@option:check", "Upload revoked certificate to %1", keyserver()));
}
ui.keyserverCheckbox->setEnabled(haveKeyserverConfigured());
ui.keyserverCheckbox->setChecked(keyserver().startsWith(QStringLiteral("ldap://")) || keyserver().startsWith(QStringLiteral("ldaps://")));
mainLayout->addWidget(ui.keyserverCheckbox);
mainLayout->addWidget(expander);
mainLayout->addStretch(1);
for (auto radio : reasonGroup.buttons()) {
reasonLayout->addWidget(radio);
}
{
ui.descriptionLabel = new QLabel{i18nc("@label:textbox", "Description (optional):"), q};
ui.description = new TextEdit{q};
ui.description->setAcceptRichText(false);
// do not accept Tab as input; this is better for accessibility and
// tabulators are not really that useful in the description
ui.description->setTabChangesFocus(true);
ui.descriptionLabel->setBuddy(ui.description);
ui.descriptionError = new ErrorLabel{q};
ui.descriptionError->setVisible(false);
reasonLayout->addWidget(ui.descriptionLabel);
reasonLayout->addWidget(ui.description);
reasonLayout->addWidget(ui.descriptionError);
}
connect(ui.description, &TextEdit::editingFinished, q, [this]() {
onDescriptionEditingFinished();
});
connect(ui.description, &TextEdit::textChanged, q, [this]() {
onDescriptionTextChanged();
});
ui.buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
auto okButton = ui.buttonBox->button(QDialogButtonBox::Ok);
okButton->setText(i18nc("@action:button", "Revoke Certificate"));
okButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete-remove")));
mainLayout->addWidget(ui.buttonBox);
connect(ui.buttonBox, &QDialogButtonBox::accepted, q, [this]() {
checkAccept();
});
connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
restoreGeometry();
}
~Private()
{
saveGeometry();
}
private:
void saveGeometry()
{
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), QStringLiteral("RevokeKeyDialog"));
cfgGroup.writeEntry("Size", q->size());
cfgGroup.sync();
}
void restoreGeometry(const QSize &defaultSize = {})
{
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), QStringLiteral("RevokeKeyDialog"));
const QSize size = cfgGroup.readEntry("Size", defaultSize);
if (size.isValid()) {
q->resize(size);
} else {
q->resize(q->minimumSizeHint());
}
}
void checkAccept()
{
if (!descriptionHasAcceptableInput()) {
KMessageBox::error(q, descriptionErrorMessage());
} else {
q->accept();
}
}
bool descriptionHasAcceptableInput() const
{
return !q->description().contains(QLatin1StringView{"\n\n"});
}
QString descriptionErrorMessage() const
{
QString message;
if (!descriptionHasAcceptableInput()) {
message = i18n("Error: The description must not contain empty lines.");
}
return message;
}
void updateDescriptionError()
{
const auto currentErrorMessage = ui.descriptionError->text();
const auto newErrorMessage = descriptionErrorMessage();
if (newErrorMessage == currentErrorMessage) {
return;
}
if (currentErrorMessage.isEmpty() && descriptionEditingInProgress) {
// delay showing the error message until editing is finished, so that we
// do not annoy the user with an error message while they are still
// entering the recipient;
// on the other hand, we clear the error message immediately if it does
// not apply anymore and we update the error message immediately if it
// changed
return;
}
ui.descriptionError->setVisible(!newErrorMessage.isEmpty());
ui.descriptionError->setText(newErrorMessage);
updateAccessibleNameAndDescription();
}
void updateAccessibleNameAndDescription()
{
// fall back to default accessible name if accessible name wasn't set explicitly
if (descriptionAccessibleName.isEmpty()) {
descriptionAccessibleName = getAccessibleName(ui.description);
}
const bool errorShown = ui.descriptionError->isVisible();
// Qt does not support "described-by" relations (like WCAG's "aria-describedby" relationship attribute);
// emulate this by setting the error message as accessible description of the input field
const auto description = errorShown ? ui.descriptionError->text() : QString{};
if (ui.description->accessibleDescription() != description) {
ui.description->setAccessibleDescription(description);
}
// Qt does not support IA2's "invalid entry" state (like WCAG's "aria-invalid" state attribute);
// screen readers say something like "invalid entry" if this state is set;
// emulate this by adding "invalid entry" to the accessible name of the input field
// and its label
const auto name = errorShown ? descriptionAccessibleName + QLatin1StringView{", "} + invalidEntryText() //
: descriptionAccessibleName;
if (ui.descriptionLabel->accessibleName() != name) {
ui.descriptionLabel->setAccessibleName(name);
}
if (ui.description->accessibleName() != name) {
ui.description->setAccessibleName(name);
}
}
void onDescriptionTextChanged()
{
descriptionEditingInProgress = true;
updateDescriptionError();
}
void onDescriptionEditingFinished()
{
descriptionEditingInProgress = false;
updateDescriptionError();
}
};
RevokeKeyDialog::RevokeKeyDialog(QWidget *parent, Qt::WindowFlags f)
: QDialog{parent, f}
, d{new Private{this}}
{
}
RevokeKeyDialog::~RevokeKeyDialog() = default;
void RevokeKeyDialog::setKey(const GpgME::Key &key)
{
d->key = key;
auto formattedKey =
QStringLiteral("%1 (%2, created %3)")
.arg(Formatting::nameAndEmailForSummaryLine(key), Formatting::prettyID(key.subkey(0).fingerprint()), Formatting::creationDateString(key))
.toHtmlEscaped();
d->ui.infoLabel->setText(
xi18nc("@info",
"<para>You are about to revoke the following certificate:</para><para>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%1</para><para><emphasis "
"strong='true'>The revocation will take effect immediately and cannot be reverted.</emphasis></para><para>Consequences: <list>"
"<item>You can still decrypt everything encrypted for this certificate.</item>"
"<item>You cannot sign anything with this certificate anymore.</item>"
"<item>You cannot certify other certificates with it anymore.</item>"
"<item>Other people can no longer encrypt with it after receiving the revocation.</item></list></para>")
.arg(formattedKey));
}
GpgME::RevocationReason RevokeKeyDialog::reason() const
{
return static_cast<RevocationReason>(d->reasonGroup.checkedId());
}
QString RevokeKeyDialog::description() const
{
static const QRegularExpression whitespaceAtEndOfLine{QStringLiteral(R"([ \t\r]+\n)")};
static const QRegularExpression trailingWhitespace{QStringLiteral(R"(\s*$)")};
return d->ui.description->toPlainText().remove(whitespaceAtEndOfLine).remove(trailingWhitespace);
}
bool RevokeKeyDialog::uploadToKeyserver() const
{
return d->ui.keyserverCheckbox->isChecked();
}
#include "revokekeydialog.moc"
#include "moc_revokekeydialog.cpp"
diff --git a/src/newcertificatewizard/enterdetailspage.cpp b/src/newcertificatewizard/enterdetailspage.cpp
index 64b651936..5d136a651 100644
--- a/src/newcertificatewizard/enterdetailspage.cpp
+++ b/src/newcertificatewizard/enterdetailspage.cpp
@@ -1,530 +1,530 @@
/* -*- mode: c++; c-basic-offset:4 -*-
newcertificatewizard/enterdetailspage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "enterdetailspage_p.h"
#include "advancedsettingsdialog_p.h"
#include "utils/scrollarea.h"
#include "utils/userinfo.h"
-#include "utils/validation.h"
#include <settings.h>
#include <Libkleo/Compat>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/OidMap>
#include <Libkleo/Stl_Util>
+#include <Libkleo/Validation>
#include <KLocalizedString>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMetaProperty>
#include <QPushButton>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <QValidator>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
using namespace GpgME;
static void set_tab_order(const QList<QWidget *> &wl)
{
kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), [](QWidget *w1, QWidget *w2) {
QWidget::setTabOrder(w1, w2);
});
}
static QString pgpLabel(const QString &attr)
{
if (attr == QLatin1StringView("NAME")) {
return i18n("Name");
}
if (attr == QLatin1StringView("EMAIL")) {
return i18n("EMail");
}
return QString();
}
static QString attributeLabel(const QString &attr, bool pgp)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = pgp ? pgpLabel(attr) : Kleo::DN::attributeNameToLabel(attr);
if (!label.isEmpty())
if (pgp) {
return label;
} else
return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr);
else {
return attr;
}
}
static QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
struct EnterDetailsPage::UI {
QGridLayout *gridLayout = nullptr;
QLabel *nameLB = nullptr;
QLineEdit *nameLE = nullptr;
QLabel *nameRequiredLB = nullptr;
QLabel *emailLB = nullptr;
QLineEdit *emailLE = nullptr;
QLabel *emailRequiredLB = nullptr;
QCheckBox *withPassCB = nullptr;
QLineEdit *resultLE = nullptr;
QLabel *errorLB = nullptr;
QPushButton *advancedPB = nullptr;
UI(QWizardPage *parent)
{
parent->setTitle(i18nc("@title", "Enter Details"));
auto mainLayout = new QVBoxLayout{parent};
const auto margins = mainLayout->contentsMargins();
mainLayout->setContentsMargins(margins.left(), 0, margins.right(), 0);
auto scrollArea = new ScrollArea{parent};
scrollArea->setFocusPolicy(Qt::NoFocus);
scrollArea->setFrameStyle(QFrame::NoFrame);
scrollArea->setBackgroundRole(parent->backgroundRole());
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
auto scrollAreaLayout = qobject_cast<QBoxLayout *>(scrollArea->widget()->layout());
scrollAreaLayout->setContentsMargins(0, margins.top(), 0, margins.bottom());
gridLayout = new QGridLayout;
int row = 0;
nameLB = new QLabel{i18n("Real name:"), parent};
nameLE = new QLineEdit{parent};
nameRequiredLB = new QLabel{i18n("(required)"), parent};
gridLayout->addWidget(nameLB, row, 0, 1, 1);
gridLayout->addWidget(nameLE, row, 1, 1, 1);
gridLayout->addWidget(nameRequiredLB, row, 2, 1, 1);
row++;
emailLB = new QLabel{i18n("EMail address:"), parent};
emailLE = new QLineEdit{parent};
emailRequiredLB = new QLabel{i18n("(required)"), parent};
gridLayout->addWidget(emailLB, row, 0, 1, 1);
gridLayout->addWidget(emailLE, row, 1, 1, 1);
gridLayout->addWidget(emailRequiredLB, row, 2, 1, 1);
row++;
withPassCB = new QCheckBox{i18n("Protect the generated key with a passphrase."), parent};
withPassCB->setToolTip(
i18nc("@info:tooltip", "Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation."));
gridLayout->addWidget(withPassCB, row, 1, 1, 2);
scrollAreaLayout->addLayout(gridLayout);
auto verticalSpacer = new QSpacerItem{20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding};
scrollAreaLayout->addItem(verticalSpacer);
resultLE = new QLineEdit{parent};
resultLE->setFrame(false);
resultLE->setAlignment(Qt::AlignCenter);
resultLE->setReadOnly(true);
scrollAreaLayout->addWidget(resultLE);
auto horizontalLayout = new QHBoxLayout;
errorLB = new QLabel{parent};
QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(errorLB->sizePolicy().hasHeightForWidth());
errorLB->setSizePolicy(sizePolicy);
QPalette palette;
QBrush brush(QColor(255, 0, 0, 255));
brush.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Active, QPalette::WindowText, brush);
palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush);
QBrush brush1(QColor(114, 114, 114, 255));
brush1.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1);
errorLB->setPalette(palette);
errorLB->setTextFormat(Qt::RichText);
horizontalLayout->addWidget(errorLB);
advancedPB = new QPushButton{i18n("Advanced Settings..."), parent};
advancedPB->setAutoDefault(false);
horizontalLayout->addWidget(advancedPB);
scrollAreaLayout->addLayout(horizontalLayout);
mainLayout->addWidget(scrollArea);
}
};
EnterDetailsPage::EnterDetailsPage(QWidget *p)
: WizardPage{p}
, ui{new UI{this}}
, dialog{new AdvancedSettingsDialog{this}}
{
setObjectName(QLatin1StringView("Kleo__NewCertificateUi__EnterDetailsPage"));
Settings settings;
if (settings.hideAdvanced()) {
setSubTitle(i18n("Please enter your personal details below."));
} else {
setSubTitle(i18n("Please enter your personal details below. If you want more control over the parameters, click on the Advanced Settings button."));
}
ui->advancedPB->setVisible(!settings.hideAdvanced());
ui->resultLE->setFocusPolicy(Qt::NoFocus);
// set errorLB to have a fixed height of two lines:
ui->errorLB->setText(QStringLiteral("2<br>1"));
ui->errorLB->setFixedHeight(ui->errorLB->minimumSizeHint().height());
ui->errorLB->clear();
connect(ui->advancedPB, &QPushButton::clicked, this, &EnterDetailsPage::slotAdvancedSettingsClicked);
connect(ui->resultLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged);
// The email doesn't necessarily show up in ui->resultLE:
connect(ui->emailLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged);
registerDialogPropertiesAsFields();
registerField(QStringLiteral("dn"), ui->resultLE);
registerField(QStringLiteral("name"), ui->nameLE);
registerField(QStringLiteral("email"), ui->emailLE);
registerField(QStringLiteral("protectedKey"), ui->withPassCB);
setCommitPage(true);
setButtonText(QWizard::CommitButton, i18nc("@action", "Create"));
const auto conf = QGpgME::cryptoConfig();
if (!conf) {
qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
return;
}
const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints");
if (entry && entry->boolValue()) {
qCDebug(KLEOPATRA_LOG) << "Disabling passphrace cb because of agent config.";
ui->withPassCB->setEnabled(false);
ui->withPassCB->setChecked(true);
} else {
const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
ui->withPassCB->setChecked(config.readEntry("WithPassphrase", false));
ui->withPassCB->setEnabled(!config.isEntryImmutable("WithPassphrase"));
}
}
EnterDetailsPage::~EnterDetailsPage() = default;
void EnterDetailsPage::initializePage()
{
updateForm();
ui->withPassCB->setVisible(false);
}
void EnterDetailsPage::cleanupPage()
{
saveValues();
}
void EnterDetailsPage::registerDialogPropertiesAsFields()
{
const QMetaObject *const mo = dialog->metaObject();
for (unsigned int i = mo->propertyOffset(), end = i + mo->propertyCount(); i != end; ++i) {
const QMetaProperty mp = mo->property(i);
if (mp.isValid()) {
registerField(QLatin1StringView(mp.name()), dialog, mp.name(), SIGNAL(accepted()));
}
}
}
void EnterDetailsPage::saveValues()
{
for (const Line &line : std::as_const(lineList)) {
savedValues[attributeFromKey(line.attr)] = line.edit->text().trimmed();
}
}
void EnterDetailsPage::clearForm()
{
qDeleteAll(dynamicWidgets);
dynamicWidgets.clear();
lineList.clear();
ui->nameLE->hide();
ui->nameLE->clear();
ui->nameLB->hide();
ui->nameRequiredLB->hide();
ui->emailLE->hide();
ui->emailLE->clear();
ui->emailLB->hide();
ui->emailRequiredLB->hide();
}
static int row_index_of(QWidget *w, QGridLayout *l)
{
const int idx = l->indexOf(w);
int r, c, rs, cs;
l->getItemPosition(idx, &r, &c, &rs, &cs);
return r;
}
static QLineEdit *
adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, const std::shared_ptr<QValidator> &validator, bool readonly, bool required)
{
Q_ASSERT(l);
Q_ASSERT(row >= 0);
Q_ASSERT(row < l->rowCount());
auto lb = qobject_cast<QLabel *>(l->itemAtPosition(row, 0)->widget());
Q_ASSERT(lb);
auto le = qobject_cast<QLineEdit *>(l->itemAtPosition(row, 1)->widget());
Q_ASSERT(le);
lb->setBuddy(le); // For better accessibility
auto reqLB = qobject_cast<QLabel *>(l->itemAtPosition(row, 2)->widget());
Q_ASSERT(reqLB);
lb->setText(i18nc("interpunctation for labels", "%1:", label));
le->setText(preset);
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
if (validator) {
le->setValidator(validator.get());
}
le->setReadOnly(readonly && le->hasAcceptableInput());
lb->show();
le->show();
reqLB->show();
return le;
}
static int add_row(QGridLayout *l, QList<QWidget *> *wl)
{
Q_ASSERT(l);
Q_ASSERT(wl);
const int row = l->rowCount();
QWidget *w1, *w2, *w3;
l->addWidget(w1 = new QLabel(l->parentWidget()), row, 0);
l->addWidget(w2 = new QLineEdit(l->parentWidget()), row, 1);
l->addWidget(w3 = new QLabel(l->parentWidget()), row, 2);
wl->push_back(w1);
wl->push_back(w2);
wl->push_back(w3);
return row;
}
void EnterDetailsPage::updateForm()
{
clearForm();
const auto settings = Kleo::Settings{};
const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
QStringList attrOrder = config.readEntry("DNAttributeOrder", QStringList());
if (attrOrder.empty()) {
attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C")
<< QStringLiteral("EMAIL!");
}
QList<QWidget *> widgets;
widgets.push_back(ui->nameLE);
widgets.push_back(ui->emailLE);
QMap<int, Line> lines;
for (const QString &rawKey : std::as_const(attrOrder)) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString preset = savedValues.value(attr, config.readEntry(attr, QString()));
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1StringView("_label"), attributeLabel(attr, false));
const QString regex = config.readEntry(attr + QLatin1StringView("_regex"));
const QString placeholder = config.readEntry(attr + QLatin1StringView{"_placeholder"});
int row;
bool known = true;
std::shared_ptr<QValidator> validator;
if (attr == QLatin1StringView("EMAIL")) {
row = row_index_of(ui->emailLE, ui->gridLayout);
validator = regex.isEmpty() ? Validation::email() : Validation::email(regex);
} else if (attr == QLatin1StringView("NAME") || attr == QLatin1String("CN")) {
if (attr == QLatin1String("NAME")) {
continue;
}
row = row_index_of(ui->nameLE, ui->gridLayout);
} else {
known = false;
row = add_row(ui->gridLayout, &dynamicWidgets);
}
if (!validator && !regex.isEmpty()) {
validator = std::make_shared<QRegularExpressionValidator>(QRegularExpression{regex});
}
QLineEdit *le = adjust_row(ui->gridLayout, row, label, preset, validator, readonly, required);
le->setPlaceholderText(placeholder);
const Line line = {key, label, regex, le, validator};
lines[row] = line;
if (!known) {
widgets.push_back(le);
}
// don't connect twice:
disconnect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
connect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel);
}
// create lineList in visual order, so requirementsAreMet()
// complains from top to bottom:
lineList.reserve(lines.count());
std::copy(lines.cbegin(), lines.cend(), std::back_inserter(lineList));
widgets.push_back(ui->withPassCB);
widgets.push_back(ui->advancedPB);
if (ui->nameLE->text().isEmpty() && settings.prefillCN()) {
ui->nameLE->setText(userFullName());
}
if (ui->emailLE->text().isEmpty() && settings.prefillEmail()) {
ui->emailLE->setText(userEmailAddress());
}
slotUpdateResultLabel();
set_tab_order(widgets);
}
QString EnterDetailsPage::cmsDN() const
{
DN dn;
for (QList<Line>::const_iterator it = lineList.begin(), end = lineList.end(); it != end; ++it) {
const QString text = it->edit->text().trimmed();
if (text.isEmpty()) {
continue;
}
QString attr = attributeFromKey(it->attr);
if (attr == QLatin1StringView("EMAIL")) {
continue;
}
if (const char *const oid = oidForAttributeName(attr)) {
attr = QString::fromUtf8(oid);
}
dn.append(DN::Attribute(attr, text));
}
return dn.dn();
}
QString EnterDetailsPage::pgpUserID() const
{
return Formatting::prettyNameAndEMail(OpenPGP, QString(), ui->nameLE->text().trimmed(), ui->emailLE->text().trimmed(), QString());
}
static bool has_intermediate_input(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return v && v->validate(text, pos) == QValidator::Intermediate;
}
static bool requirementsAreMet(const QList<EnterDetailsPage::Line> &list, QString &error)
{
bool allEmpty = true;
for (const auto &line : list) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
const QString key = line.attr;
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking" << key << "against" << le->text() << ":";
if (le->text().trimmed().isEmpty()) {
if (key.endsWith(QLatin1Char('!'))) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else
error = xi18nc("@info",
"<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
return false;
}
} else if (has_intermediate_input(le)) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else
error = xi18nc("@info",
"<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
return false;
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
error = xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else
error = xi18nc("@info",
"<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>",
line.label,
line.regex);
return false;
} else {
allEmpty = false;
}
}
// Ensure that at least one value is acceptable
return !allEmpty;
}
bool EnterDetailsPage::isComplete() const
{
QString error;
const bool ok = requirementsAreMet(lineList, error);
ui->errorLB->setText(error);
return ok;
}
void EnterDetailsPage::slotAdvancedSettingsClicked()
{
dialog->exec();
}
void EnterDetailsPage::slotUpdateResultLabel()
{
ui->resultLE->setText(cmsDN());
}
#include "moc_enterdetailspage_p.cpp"
diff --git a/src/newcertificatewizard/keyalgo.cpp b/src/newcertificatewizard/keyalgo.cpp
deleted file mode 100644
index 9dc4e0e0c..000000000
--- a/src/newcertificatewizard/keyalgo.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- newcertificatewizard/keyalgo.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
- SPDX-FileContributor: Intevation GmbH
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "keyalgo_p.h"
-
-using namespace GpgME;
-
-bool Kleo::NewCertificateUi::is_algo(GpgME::Subkey::PubkeyAlgo algo, KeyAlgo what)
-{
- switch (algo) {
- case Subkey::AlgoRSA:
- case Subkey::AlgoRSA_E:
- case Subkey::AlgoRSA_S:
- return what == RSA;
- case Subkey::AlgoELG_E:
- case Subkey::AlgoELG:
- return what == ELG;
- case Subkey::AlgoDSA:
- return what == DSA;
- case Subkey::AlgoECDSA:
- return what == ECDSA;
- case Subkey::AlgoECDH:
- return what == ECDH;
- case Subkey::AlgoEDDSA:
- return what == EDDSA;
- default:
- break;
- }
- return false;
-}
-
-bool Kleo::NewCertificateUi::is_rsa(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), RSA);
-}
-
-bool Kleo::NewCertificateUi::is_dsa(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), DSA);
-}
-
-bool Kleo::NewCertificateUi::is_elg(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ELG);
-}
-
-bool Kleo::NewCertificateUi::is_ecdsa(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDSA);
-}
-
-bool Kleo::NewCertificateUi::is_eddsa(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), EDDSA);
-}
-
-bool Kleo::NewCertificateUi::is_ecdh(unsigned int algo)
-{
- return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDH);
-}
diff --git a/src/newcertificatewizard/keyalgo_p.h b/src/newcertificatewizard/keyalgo_p.h
deleted file mode 100644
index db85f21f1..000000000
--- a/src/newcertificatewizard/keyalgo_p.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- newcertificatewizard/keyalgo_p.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
- SPDX-FileContributor: Intevation GmbH
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <gpgme++/key.h>
-
-namespace Kleo::NewCertificateUi
-{
-
-enum KeyAlgo { RSA, DSA, ELG, ECDSA, ECDH, EDDSA };
-
-bool is_algo(GpgME::Subkey::PubkeyAlgo algo, KeyAlgo what);
-
-bool is_rsa(unsigned int algo);
-bool is_dsa(unsigned int algo);
-bool is_elg(unsigned int algo);
-bool is_ecdsa(unsigned int algo);
-bool is_eddsa(unsigned int algo);
-bool is_ecdh(unsigned int algo);
-
-}
diff --git a/src/newcertificatewizard/keycreationpage.cpp b/src/newcertificatewizard/keycreationpage.cpp
index 1da1718c2..9427318c1 100644
--- a/src/newcertificatewizard/keycreationpage.cpp
+++ b/src/newcertificatewizard/keycreationpage.cpp
@@ -1,175 +1,174 @@
/* -*- mode: c++; c-basic-offset:4 -*-
newcertificatewizard/keycreationpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keycreationpage_p.h"
#include "kleopatraapplication.h"
-#include "utils/keyparameters.h"
-
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
+#include <Libkleo/KeyParameters>
#include <Libkleo/KeyUsage>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
#include <QLabel>
#include <QUrl>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/keygenerationresult.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
using namespace GpgME;
struct KeyCreationPage::UI {
UI(QWizardPage *parent)
{
parent->setTitle(i18nc("@title", "Creating Key Pair..."));
auto mainLayout = new QVBoxLayout{parent};
auto label = new QLabel{i18n("The process of creating a key requires large amounts of random numbers. This may require several minutes..."), parent};
label->setWordWrap(true);
mainLayout->addWidget(label);
}
};
KeyCreationPage::KeyCreationPage(QWidget *p)
: WizardPage{p}
, ui{new UI{this}}
{
setObjectName(QString::fromUtf8("Kleo__NewCertificateUi__KeyCreationPage"));
}
KeyCreationPage::~KeyCreationPage() = default;
bool KeyCreationPage::isComplete() const
{
return !job;
}
void KeyCreationPage::initializePage()
{
startJob();
}
void KeyCreationPage::startJob()
{
auto j = QGpgME::smime()->keyGenerationJob();
if (!j) {
return;
}
connect(j, &QGpgME::KeyGenerationJob::result, this, &KeyCreationPage::slotResult);
if (const Error err = j->start(createGnupgKeyParms()))
setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1", Formatting::errorAsString(err)));
else {
job = j;
}
}
KeyUsage KeyCreationPage::keyUsage() const
{
KeyUsage usage;
if (signingAllowed()) {
usage.setCanSign(true);
}
if (encryptionAllowed()) {
usage.setCanEncrypt(true);
}
return usage;
}
QString KeyCreationPage::createGnupgKeyParms() const
{
KeyParameters keyParameters(KeyParameters::CMS);
keyParameters.setKeyType(keyType());
if (const unsigned int strength = keyStrength()) {
keyParameters.setKeyLength(strength);
}
keyParameters.setKeyUsage(keyUsage());
keyParameters.setDN(dn());
keyParameters.setEmail(email());
const auto addesses{additionalEMailAddresses()};
for (const QString &email : addesses) {
keyParameters.addEmail(email);
}
const auto dnsN{dnsNames()};
for (const QString &dns : dnsN) {
keyParameters.addDomainName(dns);
}
const auto urisList{uris()};
for (const QString &uri : urisList) {
keyParameters.addURI(uri);
}
const QString result = keyParameters.toString();
qCDebug(KLEOPATRA_LOG) << '\n' << result;
return result;
}
void KeyCreationPage::slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog)
{
Q_UNUSED(auditLog)
if (result.error().code()) {
setField(QStringLiteral("error"),
result.error().isCanceled() ? i18n("Operation canceled.") : i18n("Could not create key pair: %1", Formatting::errorAsString(result.error())));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else {
QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10")));
if (!file.open(QIODevice::WriteOnly)) {
setField(QStringLiteral("error"), i18n("Could not write output file %1: %2", file.fileName(), file.errorString()));
setField(QStringLiteral("url"), QString());
setField(QStringLiteral("result"), QString());
} else {
file.write(request);
setField(QStringLiteral("error"), QString());
setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString());
setField(QStringLiteral("result"), i18n("Key pair created successfully."));
}
}
setField(QStringLiteral("fingerprint"), result.fingerprint() ? QString::fromLatin1(result.fingerprint()) : QString());
job = nullptr;
Q_EMIT completeChanged();
const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
if (config.readEntry("SkipResultPage", false)) {
if (result.fingerprint()) {
KleopatraApplication::instance()->slotActivateRequested(QStringList() << QStringLiteral("kleopatra") << QStringLiteral("--query")
<< QLatin1StringView(result.fingerprint()),
QString());
QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection);
} else {
QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
}
} else {
QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
}
}
#include "moc_keycreationpage_p.cpp"
diff --git a/src/utils/expiration.cpp b/src/utils/expiration.cpp
deleted file mode 100644
index 494f9b310..000000000
--- a/src/utils/expiration.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/expiration.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 <config-kleopatra.h>
-
-#include "expiration.h"
-
-#include <settings.h>
-
-#include <KDateComboBox>
-#include <KLocalizedString>
-
-QDate Kleo::maximumAllowedDate()
-{
- static const QDate maxAllowedDate{2106, 2, 5};
- return maxAllowedDate;
-}
-
-QDate Kleo::minimumExpirationDate()
-{
- return expirationDateRange().minimum;
-}
-
-QDate Kleo::maximumExpirationDate()
-{
- return expirationDateRange().maximum;
-}
-
-Kleo::DateRange Kleo::expirationDateRange()
-{
- Kleo::DateRange range;
-
- const auto settings = Kleo::Settings{};
- const auto today = QDate::currentDate();
-
- const auto minimumExpiry = std::max(1, settings.validityPeriodInDaysMin());
- range.minimum = std::min(today.addDays(minimumExpiry), maximumAllowedDate());
-
- const auto maximumExpiry = settings.validityPeriodInDaysMax();
- if (maximumExpiry >= 0) {
- range.maximum = std::min(std::max(today.addDays(maximumExpiry), range.minimum), maximumAllowedDate());
- }
-
- return range;
-}
-
-QDate Kleo::defaultExpirationDate(Kleo::ExpirationOnUnlimitedValidity onUnlimitedValidity)
-{
- QDate expirationDate;
-
- const auto settings = Kleo::Settings{};
- const auto defaultExpirationInDays = settings.validityPeriodInDays();
- if (defaultExpirationInDays > 0) {
- expirationDate = QDate::currentDate().addDays(defaultExpirationInDays);
- } else if (defaultExpirationInDays < 0 || onUnlimitedValidity == ExpirationOnUnlimitedValidity::InternalDefaultExpiration) {
- expirationDate = QDate::currentDate().addYears(3);
- }
-
- const auto allowedRange = expirationDateRange();
- expirationDate = std::max(expirationDate, allowedRange.minimum);
- if (allowedRange.maximum.isValid()) {
- expirationDate = std::min(expirationDate, allowedRange.maximum);
- }
-
- return expirationDate;
-}
-
-bool Kleo::isValidExpirationDate(const QDate &date)
-{
- const auto allowedRange = expirationDateRange();
- if (date.isValid()) {
- return (date >= allowedRange.minimum //
- && ((allowedRange.maximum.isValid() && date <= allowedRange.maximum) //
- || (!allowedRange.maximum.isValid() && date <= maximumAllowedDate())));
- } else {
- return !allowedRange.maximum.isValid();
- }
-}
-
-static QString dateToString(const QDate &date, QWidget *widget)
-{
- // workaround for QLocale using "yy" way too often for years
- // stolen from KDateComboBox
- auto locale = widget ? widget->locale() : QLocale{};
- const auto dateFormat = (locale
- .dateFormat(QLocale::ShortFormat) //
- .replace(QLatin1StringView{"yy"}, QLatin1String{"yyyy"})
- .replace(QLatin1StringView{"yyyyyyyy"}, QLatin1String{"yyyy"}));
- return locale.toString(date, dateFormat);
-}
-
-static QString validityPeriodHint(const Kleo::DateRange &dateRange, QWidget *widget)
-{
- // the minimum date is always valid
- if (dateRange.maximum.isValid()) {
- if (dateRange.maximum == dateRange.minimum) {
- return i18nc("@info", "The date cannot be changed.");
- } else {
- return i18nc("@info ... between <a date> and <another date>.",
- "Enter a date between %1 and %2.",
- dateToString(dateRange.minimum, widget),
- dateToString(dateRange.maximum, widget));
- }
- } else {
- return i18nc("@info ... between <a date> and <another date>.",
- "Enter a date between %1 and %2.",
- dateToString(dateRange.minimum, widget),
- dateToString(Kleo::maximumAllowedDate(), widget));
- }
-}
-
-QString Kleo::validityPeriodHint()
-{
- return ::validityPeriodHint(expirationDateRange(), nullptr);
-}
-
-void Kleo::setUpExpirationDateComboBox(KDateComboBox *dateCB, const Kleo::DateRange &range)
-{
- const auto dateRange = range.minimum.isValid() ? range : expirationDateRange();
- // enable warning on invalid or not allowed dates
- dateCB->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords
- | KDateComboBox::WarnOnInvalid);
- const auto hintAndErrorMessage = validityPeriodHint(dateRange, dateCB);
- dateCB->setDateRange(dateRange.minimum, dateRange.maximum.isValid() ? dateRange.maximum : maximumAllowedDate(), hintAndErrorMessage, hintAndErrorMessage);
- if (dateRange.minimum == dateRange.maximum) {
- // only one date is allowed, so that changing it no sense
- dateCB->setEnabled(false);
- }
- dateCB->setToolTip(hintAndErrorMessage);
- const QDate today = QDate::currentDate();
- dateCB->setDateMap({
- {today.addYears(3), i18nc("@item:inlistbox", "Three years from now")},
- {today.addYears(2), i18nc("@item:inlistbox", "Two years from now")},
- {today.addYears(1), i18nc("@item:inlistbox", "One year from now")},
- });
-}
diff --git a/src/utils/expiration.h b/src/utils/expiration.h
deleted file mode 100644
index 614076738..000000000
--- a/src/utils/expiration.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/expiration.h
-
- 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
-*/
-
-#pragma once
-
-#include <QDate>
-
-class KDateComboBox;
-
-namespace Kleo
-{
-struct DateRange {
- QDate minimum;
- QDate maximum;
-};
-
-/**
- * Returns a date a bit before the technically possible latest expiration
- * date (~2106-02-07) that is safe to use as latest expiration date.
- */
-QDate maximumAllowedDate();
-
-/**
- * Returns the earliest allowed expiration date.
- *
- * This is either tomorrow or the configured number of days after today
- * (whichever is later).
- *
- * \sa Settings::validityPeriodInDaysMin
- */
-QDate minimumExpirationDate();
-
-/**
- * Returns the latest allowed expiration date.
- *
- * If unlimited validity is allowed, then an invalid date is returned.
- * Otherwise, either the configured number of days after today or
- * the maximum allowed date, whichever is earlier, is returned.
- * Additionally, the returned date is never earlier than the minimum
- * expiration date.
- *
- * \sa Settings::validityPeriodInDaysMax
- */
-QDate maximumExpirationDate();
-
-/**
- * Returns the allowed range for the expiration date.
- *
- * \sa minimumExpirationDate, maximumExpirationDate
- */
-DateRange expirationDateRange();
-
-enum class ExpirationOnUnlimitedValidity {
- NoExpiration,
- InternalDefaultExpiration,
-};
-
-/**
- * Returns a useful value for the default expiration date based on the current
- * date and the configured default validity. If the configured validity is
- * unlimited, then the return value depends on \p onUnlimitedValidity.
- *
- * The returned value is always in the allowed range for the expiration date.
- *
- * \sa expirationDateRange
- */
-QDate defaultExpirationDate(ExpirationOnUnlimitedValidity onUnlimitedValidity);
-
-/**
- * Returns true, if \p date is a valid expiration date.
- */
-bool isValidExpirationDate(const QDate &date);
-
-/**
- * Returns a hint which dates are valid expiration dates for the date
- * combo box \p dateCB.
- * The hint can be used as tool tip or as error message when the user
- * entered an invalid date.
- */
-QString validityPeriodHint();
-
-/**
- * Configures the date combo box \p dateCB for choosing an expiration date.
- *
- * Sets the allowed date range to the \p dateRange, or to the configured
- * validity period range if the minimum date is invalid. If the maximum
- * date is invalid, then the maximumAllowedDate is set as maximum.
- * Also sets a tooltip and a few fixed values to choose from, enables
- * warnings on invalid or not allowed dates, and disables the combo box if
- * the date range spans a single day.
- */
-void setUpExpirationDateComboBox(KDateComboBox *dateCB, const Kleo::DateRange &dateRange = Kleo::DateRange{});
-}
diff --git a/src/utils/keyparameters.cpp b/src/utils/keyparameters.cpp
deleted file mode 100644
index 8543899f1..000000000
--- a/src/utils/keyparameters.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/keyparameters.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
-
- SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include "keyparameters.h"
-
-#include <Libkleo/KeyUsage>
-
-#include <QDate>
-#include <QUrl>
-
-#include "kleopatra_debug.h"
-
-using namespace Kleo;
-using namespace GpgME;
-
-namespace
-{
-QString encodeDomainName(const QString &domain)
-{
- const QByteArray encodedDomain = QUrl::toAce(domain);
- return encodedDomain.isEmpty() ? domain : QString::fromLatin1(encodedDomain);
-}
-
-QString encodeEmail(const QString &email)
-{
- const int at = email.lastIndexOf(QLatin1Char('@'));
- if (at < 0) {
- return email;
- }
- return email.left(at + 1) + encodeDomainName(email.mid(at + 1));
-}
-}
-
-class KeyParameters::Private
-{
- friend class ::Kleo::KeyParameters;
-
- Protocol protocol;
-
- Subkey::PubkeyAlgo keyType = Subkey::AlgoUnknown;
- QString cardKeyRef;
- unsigned int keyLength = 0;
- QString keyCurve;
- KeyUsage keyUsage;
-
- Subkey::PubkeyAlgo subkeyType = Subkey::AlgoUnknown;
- unsigned int subkeyLength = 0;
- QString subkeyCurve;
- KeyUsage subkeyUsage;
-
- QString name;
- QString comment;
- QString dn;
- std::vector<QString> emailAdresses;
- std::vector<QString> domainNames;
- std::vector<QString> uris;
-
- QDate expirationDate;
-
-public:
- explicit Private(Protocol proto)
- : protocol(proto)
- {
- }
-};
-
-KeyParameters::KeyParameters()
- : KeyParameters{NoProtocol}
-{
-}
-
-KeyParameters::KeyParameters(Protocol protocol)
- : d{new Private{protocol}}
-{
-}
-
-KeyParameters::~KeyParameters() = default;
-
-KeyParameters::KeyParameters(const KeyParameters &other)
- : d{new Private{*other.d}}
-{
-}
-
-KeyParameters &KeyParameters::operator=(const KeyParameters &other)
-{
- *d = *other.d;
- return *this;
-}
-
-KeyParameters::KeyParameters(KeyParameters &&other) = default;
-
-KeyParameters &KeyParameters::operator=(KeyParameters &&other) = default;
-
-KeyParameters::Protocol KeyParameters::protocol() const
-{
- return d->protocol;
-}
-
-void KeyParameters::setKeyType(Subkey::PubkeyAlgo type)
-{
- d->keyType = type;
-}
-
-GpgME::Subkey::PubkeyAlgo KeyParameters::keyType() const
-{
- return d->keyType;
-}
-
-void KeyParameters::setCardKeyRef(const QString &cardKeyRef)
-{
- d->cardKeyRef = cardKeyRef;
-}
-
-QString KeyParameters::cardKeyRef() const
-{
- return d->cardKeyRef;
-}
-
-void KeyParameters::setKeyLength(unsigned int length)
-{
- d->keyLength = length;
-}
-
-unsigned int KeyParameters::keyLength() const
-{
- return d->keyLength;
-}
-
-void KeyParameters::setKeyCurve(const QString &curve)
-{
- d->keyCurve = curve;
-}
-
-QString KeyParameters::keyCurve() const
-{
- return d->keyCurve;
-}
-
-void KeyParameters::setKeyUsage(const KeyUsage &usage)
-{
- d->keyUsage = usage;
-}
-
-KeyUsage KeyParameters::keyUsage() const
-{
- return d->keyUsage;
-}
-
-void KeyParameters::setSubkeyType(Subkey::PubkeyAlgo type)
-{
- d->subkeyType = type;
-}
-
-Subkey::PubkeyAlgo KeyParameters::subkeyType() const
-{
- return d->subkeyType;
-}
-
-void KeyParameters::setSubkeyLength(unsigned int length)
-{
- d->subkeyLength = length;
-}
-
-unsigned int KeyParameters::subkeyLength() const
-{
- return d->subkeyLength;
-}
-
-void KeyParameters::setSubkeyCurve(const QString &curve)
-{
- d->subkeyCurve = curve;
-}
-
-QString KeyParameters::subkeyCurve() const
-{
- return d->subkeyCurve;
-}
-
-void KeyParameters::setSubkeyUsage(const KeyUsage &usage)
-{
- d->subkeyUsage = usage;
-}
-
-KeyUsage KeyParameters::subkeyUsage() const
-{
- return d->subkeyUsage;
-}
-
-void KeyParameters::setExpirationDate(const QDate &date)
-{
- d->expirationDate = date;
-}
-
-QDate KeyParameters::expirationDate() const
-{
- return d->expirationDate;
-}
-
-void KeyParameters::setName(const QString &name)
-{
- d->name = name;
-}
-
-QString KeyParameters::name() const
-{
- return d->name;
-}
-
-void KeyParameters::setComment(const QString &comment)
-{
- d->comment = comment;
-}
-
-QString KeyParameters::comment() const
-{
- return d->comment;
-}
-
-void KeyParameters::setDN(const QString &dn)
-{
- d->dn = dn;
-}
-
-QString KeyParameters::dn() const
-{
- return d->dn;
-}
-
-void KeyParameters::setEmail(const QString &email)
-{
- d->emailAdresses = {email};
-}
-
-void KeyParameters::addEmail(const QString &email)
-{
- d->emailAdresses.push_back(email);
-}
-
-std::vector<QString> KeyParameters::emails() const
-{
- return d->emailAdresses;
-}
-
-void KeyParameters::addDomainName(const QString &domain)
-{
- d->domainNames.push_back(domain);
-}
-
-std::vector<QString> KeyParameters::domainNames() const
-{
- return d->domainNames;
-}
-
-void KeyParameters::addURI(const QString &uri)
-{
- d->uris.push_back(uri);
-}
-
-std::vector<QString> KeyParameters::uris() const
-{
- return d->uris;
-}
-
-namespace
-{
-QString serialize(Subkey::PubkeyAlgo algo)
-{
- return QString::fromLatin1(Subkey::publicKeyAlgorithmAsString(algo));
-}
-
-QString serialize(unsigned int number)
-{
- return QString::number(number);
-}
-
-QString serialize(KeyUsage keyUsage)
-{
- QStringList usages;
- if (keyUsage.canSign()) {
- usages << QStringLiteral("sign");
- }
- if (keyUsage.canEncrypt()) {
- usages << QStringLiteral("encrypt");
- }
- if (keyUsage.canAuthenticate()) {
- usages << QStringLiteral("auth");
- }
- if (keyUsage.canCertify()) {
- usages << QStringLiteral("cert");
- }
- return usages.join(QLatin1Char{' '});
-}
-
-QString serialize(const QDate &date)
-{
- return date.toString(Qt::ISODate);
-}
-
-QString serialize(const char *key, const QString &value)
-{
- return QString::fromLatin1(key) + QLatin1Char(':') + value;
-}
-}
-
-QString KeyParameters::toString() const
-{
- QStringList keyParameters;
-
- keyParameters.push_back(QLatin1StringView("<GnupgKeyParms format=\"internal\">"));
-
- if (d->protocol == OpenPGP) {
- // for backward compatibility with GnuPG 2.0 and earlier
- keyParameters.push_back(QStringLiteral("%ask-passphrase"));
- }
-
- // add Key-Type as first parameter
- if (!d->cardKeyRef.isEmpty()) {
- keyParameters.push_back(serialize("Key-Type", QLatin1StringView{"card:"} + d->cardKeyRef));
- } else if (d->keyType != Subkey::AlgoUnknown) {
- keyParameters.push_back(serialize("Key-Type", serialize(d->keyType)));
- } else {
- qCWarning(KLEOPATRA_LOG) << "KeyParameters::toString(): Key type is unset/empty";
- }
- if (d->keyLength) {
- keyParameters.push_back(serialize("Key-Length", serialize(d->keyLength)));
- }
- if (!d->keyCurve.isEmpty()) {
- keyParameters.push_back(serialize("Key-Curve", d->keyCurve));
- }
- keyParameters.push_back(serialize("Key-Usage", serialize(d->keyUsage)));
-
- if (d->subkeyType != Subkey::AlgoUnknown) {
- keyParameters.push_back(serialize("Subkey-Type", serialize(d->subkeyType)));
- if (d->subkeyUsage.value()) {
- keyParameters.push_back(serialize("Subkey-Usage", serialize(d->subkeyUsage)));
- }
- if (d->subkeyLength) {
- keyParameters.push_back(serialize("Subkey-Length", serialize(d->subkeyLength)));
- }
- if (!d->subkeyCurve.isEmpty()) {
- keyParameters.push_back(serialize("Subkey-Curve", d->subkeyCurve));
- }
- }
-
- if (d->expirationDate.isValid()) {
- keyParameters.push_back(serialize("Expire-Date", serialize(d->expirationDate)));
- }
-
- if (!d->name.isEmpty()) {
- keyParameters.push_back(serialize("Name-Real", d->name));
- }
- if (!d->comment.isEmpty()) {
- keyParameters.push_back(serialize("Name-Comment", d->comment));
- }
- if (!d->dn.isEmpty()) {
- keyParameters.push_back(serialize("Name-DN", d->dn));
- }
- std::transform(std::cbegin(d->emailAdresses), std::cend(d->emailAdresses), std::back_inserter(keyParameters), [this](const auto &email) {
- return serialize("Name-Email", (d->protocol == CMS) ? encodeEmail(email) : email);
- });
- std::transform(std::cbegin(d->domainNames), std::cend(d->domainNames), std::back_inserter(keyParameters), [](const auto &domain) {
- return serialize("Name-DNS", encodeDomainName(domain));
- });
- std::transform(std::cbegin(d->uris), std::cend(d->uris), std::back_inserter(keyParameters), [](const auto &uri) {
- return serialize("Name-URI", uri);
- });
-
- keyParameters.push_back(QLatin1StringView("</GnupgKeyParms>"));
-
- return keyParameters.join(QLatin1Char('\n'));
-}
diff --git a/src/utils/keyparameters.h b/src/utils/keyparameters.h
deleted file mode 100644
index 9eb3536bc..000000000
--- a/src/utils/keyparameters.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/keyparameters.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <gpgme++/key.h>
-
-#include <memory>
-
-class QDate;
-class QString;
-
-namespace Kleo
-{
-class KeyUsage;
-
-class KeyParameters
-{
-public:
- enum Protocol {
- NoProtocol,
- OpenPGP,
- CMS,
- };
-
- KeyParameters();
- explicit KeyParameters(Protocol protocol);
- ~KeyParameters();
-
- KeyParameters(const KeyParameters &other);
- KeyParameters &operator=(const KeyParameters &other);
-
- KeyParameters(KeyParameters &&other);
- KeyParameters &operator=(KeyParameters &&other);
-
- Protocol protocol() const;
-
- void setKeyType(GpgME::Subkey::PubkeyAlgo type);
- GpgME::Subkey::PubkeyAlgo keyType() const;
- void setCardKeyRef(const QString &cardKeyRef);
- QString cardKeyRef() const;
- void setKeyLength(unsigned int length);
- unsigned int keyLength() const;
- void setKeyCurve(const QString &curve);
- QString keyCurve() const;
- void setKeyUsage(const KeyUsage &usage);
- KeyUsage keyUsage() const;
-
- void setSubkeyType(GpgME::Subkey::PubkeyAlgo type);
- GpgME::Subkey::PubkeyAlgo subkeyType() const;
- void setSubkeyLength(unsigned int length);
- unsigned int subkeyLength() const;
- void setSubkeyCurve(const QString &curve);
- QString subkeyCurve() const;
- void setSubkeyUsage(const KeyUsage &usage);
- KeyUsage subkeyUsage() const;
-
- void setExpirationDate(const QDate &date);
- QDate expirationDate() const;
-
- void setName(const QString &name);
- QString name() const;
- void setComment(const QString &comment);
- QString comment() const;
- void setDN(const QString &dn);
- QString dn() const;
- void setEmail(const QString &email);
- void addEmail(const QString &email);
- std::vector<QString> emails() const;
- void addDomainName(const QString &domain);
- std::vector<QString> domainNames() const;
- void addURI(const QString &uri);
- std::vector<QString> uris() const;
-
- QString toString() const;
-
-private:
- class Private;
- std::unique_ptr<Private> d;
-};
-
-}
diff --git a/src/utils/multivalidator.cpp b/src/utils/multivalidator.cpp
deleted file mode 100644
index 42325ce0d..000000000
--- a/src/utils/multivalidator.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/multivalidator.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "multivalidator.h"
-
-#include <algorithm>
-#include <iterator>
-
-using namespace Kleo;
-
-MultiValidator::MultiValidator(const std::vector<std::shared_ptr<QValidator>> &validators)
- : QValidator{}
- , m_validators{validators}
-{
-}
-
-// static
-std::shared_ptr<QValidator> Kleo::MultiValidator::create(const std::vector<std::shared_ptr<QValidator>> &validators)
-{
- Q_ASSERT(std::all_of(std::begin(validators), std::end(validators), [](const auto &v) {
- return v && !v->parent();
- }));
-
- return std::shared_ptr<MultiValidator>{new MultiValidator{validators}};
-}
-
-MultiValidator::~MultiValidator() = default;
-
-void MultiValidator::fixup(QString &str) const
-{
- std::for_each(std::cbegin(m_validators), std::cend(m_validators), [&str](const auto &val) {
- val->fixup(str);
- });
-}
-
-QValidator::State MultiValidator::validate(QString &str, int &pos) const
-{
- std::vector<State> states;
- states.reserve(m_validators.size());
- std::transform(std::cbegin(m_validators), std::cend(m_validators), std::back_inserter(states), [&str, &pos](const auto &val) {
- return val->validate(str, pos);
- });
-
- if (std::any_of(std::cbegin(states), std::cend(states), [](State state) {
- return state == Invalid;
- })) {
- return Invalid;
- }
-
- if (std::all_of(std::cbegin(states), std::cend(states), [](State state) {
- return state == Acceptable;
- })) {
- return Acceptable;
- }
-
- return Intermediate;
-}
-
-#include "moc_multivalidator.cpp"
diff --git a/src/utils/multivalidator.h b/src/utils/multivalidator.h
deleted file mode 100644
index 80e77137e..000000000
--- a/src/utils/multivalidator.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/multivalidator.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <QValidator>
-
-#include <memory>
-#include <vector>
-
-namespace Kleo
-{
-
-class MultiValidator : public QValidator
-{
- Q_OBJECT
-
- explicit MultiValidator(const std::vector<std::shared_ptr<QValidator>> &validators);
-
-public:
- /**
- * Creates a combined validator from the \p validators.
- *
- * The validators must not be null and they must not have a parent.
- */
- static std::shared_ptr<QValidator> create(const std::vector<std::shared_ptr<QValidator>> &validators);
-
- ~MultiValidator() override;
-
- void fixup(QString &str) const override;
- State validate(QString &str, int &pos) const override;
-
-private:
- std::vector<std::shared_ptr<QValidator>> m_validators;
-};
-
-}
diff --git a/src/utils/validation.cpp b/src/utils/validation.cpp
deleted file mode 100644
index c6d58cc66..000000000
--- a/src/utils/validation.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/validation.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "validation.h"
-
-#include <utils/multivalidator.h>
-
-#include <KEmailAddress>
-
-#include "kleopatra_debug.h"
-
-#include <QRegularExpression>
-
-using namespace Kleo;
-
-namespace
-{
-
-template<class Validator>
-class TrimmingValidator : public Validator
-{
-public:
- using Validator::Validator;
-
- QValidator::State validate(QString &str, int &pos) const override
- {
- auto trimmed = str.trimmed();
- auto posCopy = pos;
- return Validator::validate(trimmed, posCopy);
- }
-};
-
-template<class Validator>
-class EmptyIsAcceptableValidator : public Validator
-{
-public:
- using Validator::Validator;
-
- QValidator::State validate(QString &str, int &pos) const override
- {
- if (str.isEmpty()) {
- return QValidator::Acceptable;
- }
- return Validator::validate(str, pos);
- }
-};
-
-class EMailValidator : public QValidator
-{
-public:
- EMailValidator()
- : QValidator{}
- {
- }
-
- State validate(QString &str, int &pos) const override
- {
- Q_UNUSED(pos)
- if (KEmailAddress::isValidSimpleAddress(str)) {
- return Acceptable;
- }
- return Intermediate;
- }
-};
-
-std::shared_ptr<QValidator> regularExpressionValidator(Validation::Flags flags, const QString &regexp)
-{
- if (flags & Validation::Required) {
- return std::make_shared<TrimmingValidator<QRegularExpressionValidator>>(QRegularExpression{regexp});
- } else {
- return std::make_shared<TrimmingValidator<EmptyIsAcceptableValidator<QRegularExpressionValidator>>>(QRegularExpression{regexp});
- }
-}
-
-}
-
-std::shared_ptr<QValidator> Validation::email(Flags flags)
-{
- if (flags & Required) {
- return std::make_shared<TrimmingValidator<EMailValidator>>();
- } else {
- return std::make_shared<TrimmingValidator<EmptyIsAcceptableValidator<EMailValidator>>>();
- }
-}
-
-std::shared_ptr<QValidator> Validation::email(const QString &addRX, Flags flags)
-{
- return MultiValidator::create({email(flags), regularExpressionValidator(flags, addRX)});
-}
-
-std::shared_ptr<QValidator> Validation::pgpName(Flags flags)
-{
- // this regular expression is modeled after gnupg/g10/keygen.c:ask_user_id:
- static const QString name_rx{QLatin1StringView{"[^0-9<>][^<>@]{4,}"}};
- return regularExpressionValidator(flags, name_rx);
-}
-
-std::shared_ptr<QValidator> Validation::pgpName(const QString &addRX, Flags flags)
-{
- return MultiValidator::create({pgpName(flags), regularExpressionValidator(flags, addRX)});
-}
-
-std::shared_ptr<QValidator> Validation::simpleName(Flags flags)
-{
- static const QString name_rx{QLatin1StringView{"[^<>@]*"}};
- return std::shared_ptr<QValidator>{regularExpressionValidator(flags, name_rx)};
-}
-
-std::shared_ptr<QValidator> Validation::simpleName(const QString &additionalRegExp, Flags flags)
-{
- return MultiValidator::create({simpleName(flags), regularExpressionValidator(flags, additionalRegExp)});
-}
diff --git a/src/utils/validation.h b/src/utils/validation.h
deleted file mode 100644
index 8f27ff58d..000000000
--- a/src/utils/validation.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
- utils/validation.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <memory>
-
-class QString;
-class QValidator;
-
-namespace Kleo
-{
-namespace Validation
-{
-
-enum Flags {
- Optional,
- Required,
-};
-
-std::shared_ptr<QValidator> email(Flags flags = Required);
-/**
- * Creates a validator for the name part of the user ID of an OpenPGP key with
- * restrictions that are necessary for usage with the edit-key interface.
- */
-std::shared_ptr<QValidator> pgpName(Flags flags = Required);
-/**
- * Creates a validator for the name part of the user ID of an OpenPGP key with
- * less restrictions than \ref pgpName.
- */
-std::shared_ptr<QValidator> simpleName(Flags flags = Required);
-
-std::shared_ptr<QValidator> email(const QString &additionalRegExp, Flags flags = Required);
-/**
- * Creates a validator for the name part of the user ID of an OpenPGP key with
- * restrictions that are necessary for usage with the edit-key interface, and
- * with additional restrictions imposed by \p additionalRegExp.
- */
-std::shared_ptr<QValidator> pgpName(const QString &additionalRegExp, Flags flags = Required);
-/**
- * Creates a validator for the name part of the user ID of an OpenPGP key with
- * less restrictions than \ref pgpName, but with additional restrictions imposed
- * by \p additionalRegExp.
- */
-std::shared_ptr<QValidator> simpleName(const QString &additionalRegExp, Flags flags = Required);
-
-}
-}
diff --git a/src/view/errorlabel.cpp b/src/view/errorlabel.cpp
deleted file mode 100644
index a642d90b0..000000000
--- a/src/view/errorlabel.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- view/errorlabel.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "errorlabel.h"
-
-#include <KColorScheme>
-
-using namespace Kleo;
-
-ErrorLabel::ErrorLabel(QWidget *parent)
- : QLabel{parent}
-{
- const auto colors = KColorScheme(QPalette::Active, KColorScheme::View);
- QPalette palette;
- palette.setBrush(QPalette::Window, colors.background(KColorScheme::NegativeBackground));
- palette.setBrush(QPalette::WindowText, colors.foreground(KColorScheme::NegativeText));
- setPalette(palette);
-}
-
-ErrorLabel::~ErrorLabel() = default;
-
-#include "moc_errorlabel.cpp"
diff --git a/src/view/errorlabel.h b/src/view/errorlabel.h
deleted file mode 100644
index 45211f689..000000000
--- a/src/view/errorlabel.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- view/errorlabel.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#pragma once
-
-#include <QLabel>
-
-namespace Kleo
-{
-
-class ErrorLabel : public QLabel
-{
- Q_OBJECT
-public:
- explicit ErrorLabel(QWidget *parent = nullptr);
- ~ErrorLabel() override;
-};
-
-}
diff --git a/src/view/formtextinput.cpp b/src/view/formtextinput.cpp
deleted file mode 100644
index 101df11c8..000000000
--- a/src/view/formtextinput.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-/* view/formtextinput.cpp
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include "formtextinput.h"
-
-#include "errorlabel.h"
-#include "utils/accessibility.h"
-
-#include <KLocalizedString>
-
-#include <QLabel>
-#include <QLineEdit>
-#include <QPointer>
-#include <QValidator>
-
-#include "kleopatra_debug.h"
-
-namespace
-{
-auto defaultValueRequiredErrorMessage()
-{
- return i18n("Error: Enter a value.");
-}
-
-auto defaultInvalidEntryErrorMessage()
-{
- return i18n("Error: Enter a value in the correct format.");
-}
-}
-
-namespace Kleo::_detail
-{
-
-class FormTextInputBase::Private
-{
- FormTextInputBase *q;
-
-public:
- enum Error {
- EntryOK,
- EntryMissing, // a required entry is missing
- InvalidEntry // the validator doesn't accept the entry
- };
-
- Private(FormTextInputBase *q)
- : q{q}
- , mValueRequiredErrorMessage{defaultValueRequiredErrorMessage()}
- , mInvalidEntryErrorMessage{defaultInvalidEntryErrorMessage()}
- {
- }
-
- QString annotatedIfRequired(const QString &text) const;
- void updateLabel();
- void setLabelText(const QString &text, const QString &accessibleName);
- void setHint(const QString &text, const QString &accessibleDescription);
- QString errorMessage(Error error) const;
- QString accessibleErrorMessage(Error error) const;
- void updateError();
- QString accessibleDescription() const;
- void updateAccessibleNameAndDescription();
-
- QPointer<QLabel> mLabel;
- QPointer<QLabel> mHintLabel;
- QPointer<QWidget> mWidget;
- QPointer<ErrorLabel> mErrorLabel;
- std::shared_ptr<QValidator> mValidator;
- QString mLabelText;
- QString mAccessibleName;
- QString mValueRequiredErrorMessage;
- QString mAccessibleValueRequiredErrorMessage;
- QString mInvalidEntryErrorMessage;
- QString mAccessibleInvalidEntryErrorMessage;
- Error mError = EntryOK;
- bool mRequired = false;
- bool mEditingInProgress = false;
-};
-
-QString FormTextInputBase::Private::annotatedIfRequired(const QString &text) const
-{
- return mRequired ? i18nc("@label label text (required)", "%1 (required)", text) //
- : text;
-}
-
-void FormTextInputBase::Private::updateLabel()
-{
- if (mLabel) {
- mLabel->setText(annotatedIfRequired(mLabelText));
- }
-}
-
-void FormTextInputBase::Private::setLabelText(const QString &text, const QString &accessibleName)
-{
- mLabelText = text;
- mAccessibleName = accessibleName.isEmpty() ? text : accessibleName;
- updateLabel();
- updateAccessibleNameAndDescription();
-}
-
-void FormTextInputBase::Private::setHint(const QString &text, const QString &accessibleDescription)
-{
- if (!mHintLabel) {
- return;
- }
- mHintLabel->setVisible(!text.isEmpty());
- mHintLabel->setText(text);
- mHintLabel->setAccessibleName(accessibleDescription.isEmpty() ? text : accessibleDescription);
- updateAccessibleNameAndDescription();
-}
-
-namespace
-{
-QString decoratedError(const QString &text)
-{
- return text.isEmpty() ? QString() : i18nc("@info", "Error: %1", text);
-}
-}
-
-QString FormTextInputBase::Private::errorMessage(Error error) const
-{
- switch (error) {
- case EntryOK:
- return {};
- case EntryMissing:
- return mValueRequiredErrorMessage;
- case InvalidEntry:
- return mInvalidEntryErrorMessage;
- }
- return {};
-}
-
-QString FormTextInputBase::Private::accessibleErrorMessage(Error error) const
-{
- switch (error) {
- case EntryOK:
- return {};
- case EntryMissing:
- return mAccessibleValueRequiredErrorMessage;
- case InvalidEntry:
- return mAccessibleInvalidEntryErrorMessage;
- }
- return {};
-}
-
-void FormTextInputBase::Private::updateError()
-{
- if (!mErrorLabel) {
- return;
- }
-
- if (mRequired && !q->hasValue()) {
- mError = EntryMissing;
- } else if (!q->hasAcceptableInput()) {
- mError = InvalidEntry;
- } else {
- mError = EntryOK;
- }
-
- const auto currentErrorMessage = mErrorLabel->text();
- const auto newErrorMessage = decoratedError(errorMessage(mError));
- if (newErrorMessage == currentErrorMessage) {
- return;
- }
- if (currentErrorMessage.isEmpty() && mEditingInProgress) {
- // delay showing the error message until editing is finished, so that we
- // do not annoy the user with an error message while they are still
- // entering the recipient;
- // on the other hand, we clear the error message immediately if it does
- // not apply anymore and we update the error message immediately if it
- // changed
- return;
- }
- mErrorLabel->setVisible(!newErrorMessage.isEmpty());
- mErrorLabel->setText(newErrorMessage);
- mErrorLabel->setAccessibleName(decoratedError(accessibleErrorMessage(mError)));
- updateAccessibleNameAndDescription();
-}
-
-QString FormTextInputBase::Private::accessibleDescription() const
-{
- QString description;
- if (mHintLabel) {
- // get the explicitly set accessible hint text
- description = mHintLabel->accessibleName();
- }
- if (description.isEmpty()) {
- // fall back to the default accessible description of the input widget
- description = getAccessibleDescription(mWidget);
- }
- return description;
-}
-
-void FormTextInputBase::Private::updateAccessibleNameAndDescription()
-{
- // fall back to default accessible name if accessible name wasn't set explicitly
- if (mAccessibleName.isEmpty()) {
- mAccessibleName = getAccessibleName(mWidget);
- }
- const bool errorShown = mErrorLabel && mErrorLabel->isVisible();
-
- // Qt does not support "described-by" relations (like WCAG's "aria-describedby" relationship attribute);
- // emulate this by setting the hint text and, if the error is shown, the error message as accessible
- // description of the input field
- const auto description = errorShown ? accessibleDescription() + QLatin1StringView{" "} + mErrorLabel->accessibleName() //
- : accessibleDescription();
- if (mWidget && mWidget->accessibleDescription() != description) {
- mWidget->setAccessibleDescription(description);
- }
-
- // Qt does not support IA2's "invalid entry" state (like WCAG's "aria-invalid" state attribute);
- // screen readers say something like "invalid entry" if this state is set;
- // emulate this by adding "invalid entry" to the accessible name of the input field
- // and its label
- QString name = annotatedIfRequired(mAccessibleName);
- if (errorShown) {
- name += QLatin1StringView{", "} + invalidEntryText();
- };
- if (mLabel && mLabel->accessibleName() != name) {
- mLabel->setAccessibleName(name);
- }
- if (mWidget && mWidget->accessibleName() != name) {
- mWidget->setAccessibleName(name);
- }
-}
-
-FormTextInputBase::FormTextInputBase()
- : d{new Private{this}}
-{
-}
-
-FormTextInputBase::~FormTextInputBase() = default;
-
-QWidget *FormTextInputBase::widget() const
-{
- return d->mWidget;
-}
-
-QLabel *FormTextInputBase::label() const
-{
- return d->mLabel;
-}
-
-QLabel *FormTextInputBase::hintLabel() const
-{
- return d->mHintLabel;
-}
-
-ErrorLabel *FormTextInputBase::errorLabel() const
-{
- return d->mErrorLabel;
-}
-
-void FormTextInputBase::setLabelText(const QString &text, const QString &accessibleName)
-{
- d->setLabelText(text, accessibleName);
-}
-
-void FormTextInputBase::setHint(const QString &text, const QString &accessibleDescription)
-{
- d->setHint(text, accessibleDescription);
-}
-
-void FormTextInputBase::setIsRequired(bool required)
-{
- d->mRequired = required;
- d->updateLabel();
- d->updateAccessibleNameAndDescription();
-}
-
-bool FormTextInputBase::isRequired() const
-{
- return d->mRequired;
-}
-
-void FormTextInputBase::setValidator(const std::shared_ptr<QValidator> &validator)
-{
- Q_ASSERT(!validator || !validator->parent());
-
- d->mValidator = validator;
-}
-
-void FormTextInputBase::setValueRequiredErrorMessage(const QString &text, const QString &accessibleText)
-{
- if (text.isEmpty()) {
- d->mValueRequiredErrorMessage = defaultValueRequiredErrorMessage();
- } else {
- d->mValueRequiredErrorMessage = text;
- }
- if (accessibleText.isEmpty()) {
- d->mAccessibleValueRequiredErrorMessage = d->mValueRequiredErrorMessage;
- } else {
- d->mAccessibleValueRequiredErrorMessage = accessibleText;
- }
-}
-
-void FormTextInputBase::setInvalidEntryErrorMessage(const QString &text, const QString &accessibleText)
-{
- if (text.isEmpty()) {
- d->mInvalidEntryErrorMessage = defaultInvalidEntryErrorMessage();
- } else {
- d->mInvalidEntryErrorMessage = text;
- }
- if (accessibleText.isEmpty()) {
- d->mAccessibleInvalidEntryErrorMessage = d->mInvalidEntryErrorMessage;
- } else {
- d->mAccessibleInvalidEntryErrorMessage = accessibleText;
- }
-}
-
-void FormTextInputBase::setToolTip(const QString &toolTip)
-{
- if (d->mLabel) {
- d->mLabel->setToolTip(toolTip);
- }
- if (d->mWidget) {
- d->mWidget->setToolTip(toolTip);
- }
-}
-
-void FormTextInputBase::setWidget(QWidget *widget)
-{
- auto parent = widget ? widget->parentWidget() : nullptr;
- d->mWidget = widget;
- d->mLabel = new QLabel{parent};
- d->mLabel->setTextFormat(Qt::PlainText);
- d->mLabel->setWordWrap(true);
- QFont font = d->mLabel->font();
- font.setBold(true);
- d->mLabel->setFont(font);
- d->mLabel->setBuddy(d->mWidget);
- d->mHintLabel = new QLabel{parent};
- d->mHintLabel->setWordWrap(true);
- d->mHintLabel->setTextFormat(Qt::PlainText);
- // set widget as buddy of hint label, so that the label isn't considered unrelated
- d->mHintLabel->setBuddy(d->mWidget);
- d->mHintLabel->setVisible(false);
- d->mErrorLabel = new ErrorLabel{parent};
- d->mErrorLabel->setWordWrap(true);
- d->mErrorLabel->setTextFormat(Qt::PlainText);
- // set widget as buddy of error label, so that the label isn't considered unrelated
- d->mErrorLabel->setBuddy(d->mWidget);
- d->mErrorLabel->setVisible(false);
- connectWidget();
-}
-
-void FormTextInputBase::setEnabled(bool enabled)
-{
- if (d->mLabel) {
- d->mLabel->setEnabled(enabled);
- }
- if (d->mWidget) {
- d->mWidget->setEnabled(enabled);
- }
- if (d->mErrorLabel) {
- d->mErrorLabel->setVisible(enabled && !d->mErrorLabel->text().isEmpty());
- }
-}
-
-QString FormTextInputBase::currentError() const
-{
- if (d->mError) {
- return d->errorMessage(d->mError);
- }
- return {};
-}
-
-bool FormTextInputBase::validate(const QString &text, int pos) const
-{
- QString textCopy = text;
- if (d->mValidator && d->mValidator->validate(textCopy, pos) != QValidator::Acceptable) {
- return false;
- }
- return true;
-}
-
-void FormTextInputBase::onTextChanged()
-{
- d->mEditingInProgress = true;
- d->updateError();
-}
-
-void FormTextInputBase::onEditingFinished()
-{
- d->mEditingInProgress = false;
- d->updateError();
-}
-
-}
-
-template<>
-bool Kleo::FormTextInput<QLineEdit>::hasValue() const
-{
- const auto w = widget();
- return w && !w->text().trimmed().isEmpty();
-}
-
-template<>
-bool Kleo::FormTextInput<QLineEdit>::hasAcceptableInput() const
-{
- const auto w = widget();
- return w && validate(w->text(), w->cursorPosition());
-}
-
-template<>
-void Kleo::FormTextInput<QLineEdit>::connectWidget()
-{
- const auto w = widget();
- QObject::connect(w, &QLineEdit::editingFinished, w, [this]() {
- onEditingFinished();
- });
- QObject::connect(w, &QLineEdit::textChanged, w, [this]() {
- onTextChanged();
- });
-}
diff --git a/src/view/formtextinput.h b/src/view/formtextinput.h
deleted file mode 100644
index bba06f3b2..000000000
--- a/src/view/formtextinput.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/* view/formtextinput.h
-
- This file is part of Kleopatra, the KDE keymanager
- SPDX-FileCopyrightText: 2022 g10 Code GmbH
- SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-#pragma once
-
-#include <QString>
-
-#include <memory>
-
-class QLabel;
-class QLineEdit;
-class QValidator;
-class QWidget;
-
-namespace Kleo
-{
-class ErrorLabel;
-
-namespace _detail
-{
-class FormTextInputBase
-{
-protected:
- FormTextInputBase();
-
-public:
- virtual ~FormTextInputBase();
- FormTextInputBase(const FormTextInputBase &) = delete;
- FormTextInputBase &operator=(const FormTextInputBase &) = delete;
- FormTextInputBase(FormTextInputBase &&) = delete;
- FormTextInputBase &operator=(FormTextInputBase &&) = delete;
-
- /**
- * Returns the label associated to the controlled widget. Use it to add
- * the label to a layout, but do not use it to set properties of the label
- * for which this class provides setters.
- */
- QLabel *label() const;
-
- /**
- * Returns the hint label associated to the controlled widget.
- */
- QLabel *hintLabel() const;
-
- /**
- * Returns the error label associated to the controlled widget.
- */
- ErrorLabel *errorLabel() const;
-
- /**
- * Sets \p text as text of the label and \p accessibleName as alternative
- * text for assistive tools. If \p accessibleName is empty, then \p text is
- * used instead. Both texts must be plain text.
- *
- * Note: If input is required, then the label is annotated appropriately.
- */
- void setLabelText(const QString &text, const QString &accessibleName = {});
-
- /**
- * Sets \p text as hint text for this input field and \p accessibleDescription
- * as alternative text for assistive tools. If \p accessibleDescription is
- * empty, then \p text is used instead. Both texts must be plain text.
- */
- void setHint(const QString &text, const QString &accessibleDescription = {});
-
- /**
- * Marks this input field as required.
- */
- void setIsRequired(bool required);
-
- /**
- * Returns \c true, if this field needs to be filled out.
- */
- bool isRequired() const;
-
- /**
- * Sets the validator to use for validating the input.
- *
- * Note: If you wrap a QLineEdit, then do not set a validator (or an input mask)
- * on it because this will break the correct displaying of the error message.
- */
- void setValidator(const std::shared_ptr<QValidator> &validator);
-
- /**
- * Sets \p text as error message to display if a value is required for the
- * input field, but if no value has been entered. If \p text is empty, then
- * a default message will be used. Both texts must be plain text.
- * The optional \p accessibleText is used as alternative text for assistive
- * tools.
- */
- void setValueRequiredErrorMessage(const QString &text, const QString &accessibleText = {});
-
- /**
- * Sets \p text as error message to display if the entered value is not accepted
- * by the validator. If \p text is empty, then a default message will be used.
- * The optional \p accessibleText is used as alternative text for assistive
- * tools. Both texts must be plain text.
- */
- void setInvalidEntryErrorMessage(const QString &text, const QString &accessibleText = {});
-
- /**
- * Sets the tool tip of the controlled widget and its associated label.
- */
- void setToolTip(const QString &toolTip);
-
- /**
- * Enables or disables the controlled widget and its associated label.
- * If the widget is disables, then the error label is hidden. Otherwise,
- * the error label is shown if there is an error.
- */
- void setEnabled(bool enabled);
-
- /**
- * Returns the currently shown error message for this input field.
- */
- QString currentError() const;
-
- /**
- * Returns \c true, if the input has a value. This function is used to
- * check required input fields for non-empty user input.
- * Needs to be implemented for concrete widget classes.
- * \sa validate
- */
- virtual bool hasValue() const = 0;
-
- /**
- * Returns \c true, if the input satisfies the validator.
- * Needs to be implemented for concrete widget classes.
- * \sa validate
- */
- virtual bool hasAcceptableInput() const = 0;
-
-protected:
- /**
- * Connects the slots \ref onTextChanged and \ref onEditingFinished to the
- * corresponding signal of the controlled widget.
- * Needs to be implemented for concrete widget classes.
- */
- virtual void connectWidget() = 0;
-
- /**
- * Sets the controlled widget and creates the associated labels.
- */
- void setWidget(QWidget *widget);
-
- /**
- * Returns the controlled widget.
- */
- QWidget *widget() const;
-
- /**
- * Validates \p text with the validator. Should be used when implementing
- * \ref hasAcceptableInput.
- */
- bool validate(const QString &text, int pos) const;
-
- /**
- * This slot needs to be connected to a signal of the controlled widget
- * that is emitted when the text changes like \ref QLineEdit::textChanged.
- * \sa connectWidget
- */
- void onTextChanged();
-
- /**
- * This slot needs to be connected to a signal of the controlled widget
- * that is emitted when the widget loses focus (or some user interaction
- * signals that they want to commit the entered text) like
- * \ref QLineEdit::editingFinished.
- * \sa connectWidget
- */
- void onEditingFinished();
-
-private:
- class Private;
- const std::unique_ptr<Private> d;
-};
-}
-
-/**
- * FormTextInput is a class for simplifying the management of text input widgets
- * like QLineEdit or QTextEdit with associated label and error message for usage
- * in form-like dialogs.
- *
- * Usage hints:
- * * If you wrap a QLineEdit, then do not set a validator (or an input mask)
- * on it. Instead set the validator on this class.
- * If you set a validator on the QLineEdit, then showing the error message
- * when editing is finished does not work because QLineEdit doesn't emit the
- * editingFinished() signal if the input is not acceptable.
- */
-template<class Widget>
-class FormTextInput : public _detail::FormTextInputBase
-{
- /**
- * Use \ref create to create a new instance.
- */
- FormTextInput() = default;
-
-public:
- /**
- * Creates a new instance of this class with a new instance of \p Widget.
- */
- static auto create(QWidget *parent)
- {
- std::unique_ptr<FormTextInput> self{new FormTextInput};
- self->setWidget(new Widget{parent});
- return self;
- }
-
- /**
- * Returns the controlled widget.
- */
- Widget *widget() const
- {
- return static_cast<Widget *>(FormTextInputBase::widget());
- }
-
- bool hasValue() const override;
-
- bool hasAcceptableInput() const override;
-
-private:
- void connectWidget() override;
-};
-
-template<>
-bool FormTextInput<QLineEdit>::hasValue() const;
-
-template<>
-bool FormTextInput<QLineEdit>::hasAcceptableInput() const;
-
-template<>
-void FormTextInput<QLineEdit>::connectWidget();
-
-}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 17, 1:15 AM (21 h, 24 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
65/32/271a3df2edc66b8f452382c93cc6

Event Timeline