Page MenuHome GnuPG

No OneTemporary

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7c0f0f23c..aa2ef4787 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,252 +1,252 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.27)
set(RELEASE_SERVICE_VERSION_MAJOR "26")
set(RELEASE_SERVICE_VERSION_MINOR "03")
set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
# KLEOPATRA_VERSION_STRING is used for the About data; Gpg4win sets it to a custom value
if (NOT KLEOPATRA_VERSION_STRING)
set(KLEOPATRA_VERSION_STRING "${RELEASE_SERVICE_VERSION}")
endif()
if (KLEOPATRA_VERSION_STRING MATCHES "([0-9]+\\.[0-9]+\\.[0-9]+)")
set(kleopatra_version "${CMAKE_MATCH_1}")
else()
message(WARNING "KLEOPATRA_VERSION_STRING doesn't contain a valid version number; using RELEASE_SERVICE_VERSION as version number")
set(kleopatra_version "${RELEASE_SERVICE_VERSION}")
endif()
# drop leading 0s from values to avoid bogus octal values in C/C++ e.g. with 08 or 09
string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" kleopatra_version_major "${kleopatra_version}")
string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" kleopatra_version_minor "${kleopatra_version}")
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" kleopatra_version_micro "${kleopatra_version}")
# The following is for Windows
set(kleopatra_version_win "${kleopatra_version_major}.${kleopatra_version_minor}.${kleopatra_version_micro}")
set(kleopatra_fileversion_win "${kleopatra_version_major},${kleopatra_version_minor},${kleopatra_version_micro},0")
if (NOT KLEOPATRA_DISTRIBUTION_TEXT)
# This is only used on Windows for the file attributes of Kleopatra
set(KLEOPATRA_DISTRIBUTION_TEXT "KDE")
endif()
if (NOT KLEOPATRA_ORGANIZATION_DOMAIN)
# This is used to allow multiple flavors of Kleopatra to run at the same time on Windows
set(KLEOPATRA_ORGANIZATION_DOMAIN "kde.org")
endif()
if (NOT KLEOPATRA_ORGANIZATION_NAME)
# The organization name is used on Windows to look up config entries in the registry
set(KLEOPATRA_ORGANIZATION_NAME "KDE")
endif()
if (KLEOPATRA_LIST_AS_COMPONENT)
set(KLEOPATRA_LIST_AS_COMPONENT 1)
endif()
project(kleopatra VERSION ${kleopatra_version})
option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF_MIN_VERSION "6.22.0")
set(KIDENTITYMANAGEMENT_VERSION "6.6.40")
set(KMAILTRANSPORT_VERSION "6.6.40")
set(AKONADI_MIME_VERSION "6.6.40")
set(KMIME_VERSION "6.6.40")
-set(LIBKLEO_VERSION "6.6.46")
+set(LIBKLEO_VERSION "6.6.47")
set(QT_REQUIRED_VERSION "6.7.0")
set(MIMETREEPARSER_VERSION "6.6.40")
set(GPGME_REQUIRED_VERSION "2.0.0")
set(LIBASSUAN_REQUIRED_VERSION "2.4.2")
set(GPG_ERROR_REQUIRED_VERSION "1.47")
if (WIN32)
set(KF6_WANT_VERSION ${KF_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
else ()
set(KF6_WANT_VERSION ${KF_MIN_VERSION})
set(KMIME_WANT_VERSION ${KMIME_VERSION})
endif ()
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
find_package(ECM ${KF6_WANT_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMAddTests)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
include(ECMFeatureSummary)
include(KDEClangFormat)
include(KDEGitCommitHooks)
# Find KF6 packages
find_package(KF6 ${KF6_WANT_VERSION}
REQUIRED COMPONENTS
Codecs
ColorScheme
Config
CoreAddons
Crash
I18n
IconThemes
ItemModels
KIO
WidgetsAddons
WindowSystem
XmlGui
OPTIONAL_COMPONENTS
DocTools
)
set_package_properties(KF6DocTools PROPERTIES
DESCRIPTION "Documentation tools"
PURPOSE "Required to generate Kleopatra documentation."
TYPE OPTIONAL)
# Optional packages
# shall we use DBus?
# enabled per default on Linux & BSD systems
set(USE_DBUS_DEFAULT OFF)
if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
set(USE_DBUS_DEFAULT ON)
endif()
option(USE_DBUS "Build components using DBus (used for interprocess communication, external open in same instance and various presentation related bits)" ${USE_DBUS_DEFAULT})
if(USE_DBUS)
find_package(KF6DBusAddons ${KF6_WANT_VERSION} CONFIG REQUIRED)
set(_kleopatra_dbusaddons_libs KF6::DBusAddons)
set(HAVE_QDBUS 1)
endif()
find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
set(QGPGME_NAME "QGpgmeQt6")
find_package(${QGPGME_NAME} ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
find_package(KPim6Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KPim6Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(KPim6IdentityManagementCore ${KIDENTITYMANAGEMENT_VERSION} CONFIG)
find_package(KPim6MailTransport ${KMAILTRANSPORT_VERSION} CONFIG)
find_package(KPim6AkonadiMime ${AKONADI_MIME_VERSION} CONFIG)
find_package(KPim6MimeTreeParserWidgets ${MIMETREEPARSER_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${LIBKLEO_MODULE_PATH} ${CMAKE_MODULE_PATH})
find_package(Qt6 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
find_package(LibAssuan ${LIBASSUAN_REQUIRED_VERSION} REQUIRED)
set_package_properties(LibAssuan PROPERTIES
TYPE REQUIRED
PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server"
)
find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED)
if(EXISTS ${CMAKE_SOURCE_DIR}/.git)
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE rc
ERROR_QUIET)
if(rc EQUAL 0)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_REVISION)
string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}")
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%cI ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE)
string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6"
Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}")
set(KLEOPATRA_VERSION_STRING "${KLEOPATRA_VERSION_STRING}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}")
endif()
endif()
endif()
if(EXISTS ${CMAKE_SOURCE_DIR}/VERSION)
file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION lines)
list(GET lines 1 kleopatra_commit_id)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.in ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
add_definitions(-DQT_NO_CONTEXTLESS_CONNECT)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Not setting for GNU due to too many warnings related to private members of base classes or around lambdas
# see e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56556 or https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79328
string(APPEND CMAKE_CXX_FLAGS " -Wshadow")
endif()
ecm_set_disabled_deprecation_versions(QT 6.11.0 KF 6.23.0 KMIME 6.7.0)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers")
endif()
if(MINGW)
# we do not care about different signedness of passed pointer arguments
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-pointer-sign>)
endif()
add_definitions(-DQT_NO_EMIT)
remove_definitions(-DQT_NO_FOREACH)
# Disable the use of QStringBuilder for operator+ to prevent crashes when
# returning the result of concatenating string temporaries in lambdas. We do
# this for example in some std::transform expressions.
# This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066
# Alternatively, one would always have to remember to force the lambdas to
# return a QString instead of QStringBuilder, but that's just too easy to
# forget and, unfortunately, the compiler doesn't issue a warning if one forgets
# this. So, it's just too dangerous.
# One can still use QStringBuilder explicitly with the operator% if necessary.
remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS)
remove_definitions(-DQT_USE_QSTRINGBUILDER)
kde_enable_exceptions()
option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
if (USE_UNITY_CMAKE_SUPPORT)
set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
endif()
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(autotests)
endif()
ecm_qt_install_logging_categories(
EXPORT KLEOPATRA
FILE kleopatra.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
ki18n_install(po)
if(KF6DocTools_FOUND)
kdoctools_install(po)
add_subdirectory(doc)
endif()
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
# add clang-format target for all our real source files
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
diff --git a/src/conf/dirservconfigpage.cpp b/src/conf/dirservconfigpage.cpp
index 1c3d2964e..2328cf542 100644
--- a/src/conf/dirservconfigpage.cpp
+++ b/src/conf/dirservconfigpage.cpp
@@ -1,561 +1,571 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2004, 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 "dirservconfigpage.h"
#include "labelledwidget.h"
#include <settings.h>
+#include <Libkleo/Algorithm>
#include <Libkleo/Compat>
#include <Libkleo/DirectoryServicesWidget>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyserverConfig>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include "kleopatra_debug.h"
#include <KConfig>
#include <KLocalizedString>
#include <KMessageBox>
#include <QSpinBox>
#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QTimeEdit>
#include <QVBoxLayout>
#include <gpgme++/engineinfo.h>
#include <gpgme.h>
using namespace Kleo;
using namespace QGpgME;
using namespace Qt::StringLiterals;
// Option for configuring X.509 servers (available via gpgconf since GnuPG 2.3.5 and 2.2.34)
static const char s_x509services_componentName[] = "dirmngr";
static const char s_x509services_entryName[] = "ldapserver";
// Legacy option for configuring X.509 servers (deprecated with GnuPG 2.2.28 and 2.3.2)
static const char s_x509services_legacy_componentName[] = "gpgsm";
static const char s_x509services_legacy_entryName[] = "keyserver";
static const char s_pgpservice_componentName[] = "dirmngr";
static const char s_pgpservice_entryName[] = "keyserver";
// legacy config entry used until GnuPG 2.2
static const char s_pgpservice_legacy_componentName[] = "gpg";
static const char s_pgpservice_legacy_entryName[] = "keyserver";
static const char s_timeout_componentName[] = "dirmngr";
static const char s_timeout_entryName[] = "ldaptimeout";
static const char s_maxitems_componentName[] = "dirmngr";
static const char s_maxitems_entryName[] = "max-replies";
class DirectoryServicesConfigurationPage::Private
{
DirectoryServicesConfigurationPage *q = nullptr;
public:
Private(DirectoryServicesConfigurationPage *q);
void load();
void save();
void defaults();
private:
enum EntryMultiplicity {
SingleValue,
ListValue,
};
enum ShowError {
DoNotShowError,
DoShowError,
};
void setX509ServerEntry(const std::vector<KeyserverConfig> &servers);
void load(const Kleo::Settings &settings);
QGpgME::CryptoConfigEntry *configEntry(const char *componentName,
const char *entryName,
QGpgME::CryptoConfigEntry::ArgType argType,
EntryMultiplicity multiplicity,
ShowError showError);
Kleo::LabelledWidget<QLineEdit> mOpenPGPKeyserverEdit;
Kleo::DirectoryServicesWidget *mDirectoryServices = nullptr;
Kleo::LabelledWidget<QTimeEdit> mTimeout;
Kleo::LabelledWidget<QSpinBox> mMaxItems;
QCheckBox *mFetchMissingSignerKeysCB = nullptr;
QCheckBox *mQueryWKDsForAllUserIDsCB = nullptr;
QCheckBox *mUseKeyServerCheckBox = nullptr;
QCheckBox *mRetrieveKeysCheckBox = nullptr;
QString mRememberedKeyserver;
QString mDefaultKeyserver;
QGpgME::CryptoConfigEntry *mOpenPGPServiceEntry = nullptr;
QGpgME::CryptoConfigEntry *mTimeoutConfigEntry = nullptr;
QGpgME::CryptoConfigEntry *mMaxItemsConfigEntry = nullptr;
QGpgME::CryptoConfigEntry *mRetrieveKeysEntry = nullptr;
QGpgME::CryptoConfig *mConfig = nullptr;
};
static QString canonicalizedKeyserver(const QString &keyserver)
{
if (keyserver.endsWith("://none"_L1)) {
// map hkps://none, etc., to "none"; see https://dev.gnupg.org/T6708
return u"none"_s;
}
return keyserver;
}
static QString displayKeyserver(const QString &keyserver)
{
return (keyserver == "none"_L1) ? QString{} : keyserver;
}
static QString effectiveKeyserver(const QString &keyserver, const QString &defaultKeyserver)
{
if (keyserver == "none"_L1) {
return {};
}
if (keyserver.isEmpty()) {
return (defaultKeyserver == "none"_L1) ? QString{} : defaultKeyserver;
}
return keyserver;
}
DirectoryServicesConfigurationPage::Private::Private(DirectoryServicesConfigurationPage *qq)
: q{qq}
{
mConfig = QGpgME::cryptoConfig();
const auto *const keyserverEntry =
configEntry(s_pgpservice_componentName, s_pgpservice_entryName, CryptoConfigEntry::ArgType_String, SingleValue, DoNotShowError);
if (keyserverEntry && !keyserverEntry->defaultValue().isNull()) {
mDefaultKeyserver = canonicalizedKeyserver(keyserverEntry->defaultValue().toString());
} else if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.16") {
mDefaultKeyserver = u"hkp://keys.gnupg.net"_s;
} else {
mDefaultKeyserver = u"hkps://hkps.pool.sks-keyservers.net"_s;
}
auto glay = new QGridLayout(q);
// OpenPGP keyserver
int row = 0;
{
auto groupBox = new QGroupBox{i18nc("@title", "OpenPGP Keyserver")};
groupBox->setFlat(true);
auto groupBoxLayout = new QVBoxLayout{groupBox};
groupBoxLayout->setContentsMargins({});
groupBoxLayout->addWidget(new QLabel(i18nc("@label:textbox", "Please note that only one OpenPGP keyserver can be configured.")));
{
mUseKeyServerCheckBox = new QCheckBox(i18nc("@label:checkbox", "Use OpenPGP keyserver"));
groupBoxLayout->addWidget(mUseKeyServerCheckBox);
}
{
auto l = new QHBoxLayout{};
l->setContentsMargins(0, 0, 0, 0);
groupBoxLayout->addLayout(l);
mOpenPGPKeyserverEdit.createWidgets(q);
mOpenPGPKeyserverEdit.label()->setText(i18n("OpenPGP keyserver:"));
if (engineIsVersion(2, 4, 4) //
|| (engineIsVersion(2, 2, 42) && !engineIsVersion(2, 3, 0))) {
if (mDefaultKeyserver == "none"_L1) {
mOpenPGPKeyserverEdit.widget()->setToolTip( //
xi18nc("@info:tooltip",
"Enter the address of the keyserver to use when searching for OpenPGP certificates and "
"when uploading OpenPGP certificates."));
} else {
mOpenPGPKeyserverEdit.widget()->setToolTip( //
xi18nc("@info:tooltip",
"Enter the address of the keyserver to use when searching for OpenPGP certificates and "
"when uploading OpenPGP certificates. If you do not enter an address then an internal "
"default will be used."));
}
}
l->addWidget(mOpenPGPKeyserverEdit.label());
l->addWidget(mOpenPGPKeyserverEdit.widget());
}
connect(mUseKeyServerCheckBox, &QCheckBox::toggled, mOpenPGPKeyserverEdit.widget(), &QLineEdit::setEnabled);
connect(mUseKeyServerCheckBox, &QCheckBox::toggled, q, [this]() {
if (!mUseKeyServerCheckBox->isChecked()) {
mRememberedKeyserver = mOpenPGPKeyserverEdit.widget()->text();
mOpenPGPKeyserverEdit.widget()->clear();
mOpenPGPKeyserverEdit.widget()->setPlaceholderText({});
} else {
mOpenPGPKeyserverEdit.widget()->setText(mRememberedKeyserver);
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(displayKeyserver(mDefaultKeyserver));
}
Q_EMIT q->changed();
});
{
mRetrieveKeysCheckBox = new QCheckBox(i18nc("@label:checkbox", "Search missing keys when verifying a signature"));
groupBoxLayout->addWidget(mRetrieveKeysCheckBox);
mRetrieveKeysEntry = configEntry("gpg", "auto-key-retrieve", CryptoConfigEntry::ArgType_None, SingleValue, DoNotShowError);
mRetrieveKeysCheckBox->setEnabled(!mRetrieveKeysEntry->isReadOnly());
connect(mRetrieveKeysCheckBox, &QCheckBox::toggled, q, [this]() {
Q_EMIT q->changed();
});
}
glay->addWidget(groupBox, row, 0, 1, 3);
connect(mOpenPGPKeyserverEdit.widget(), &QLineEdit::textEdited, q, &DirectoryServicesConfigurationPage::changed);
}
// X.509 servers
if (Settings{}.cmsEnabled()) {
++row;
auto groupBox = new QGroupBox{i18n("X.509 Directory Services"), q};
groupBox->setFlat(true);
auto groupBoxLayout = new QVBoxLayout{groupBox};
groupBoxLayout->setContentsMargins({});
mDirectoryServices = new Kleo::DirectoryServicesWidget(q);
// set a non-empty accessible name to prevent Qt from reporting the parent's group box title as accessible name
mDirectoryServices->setAccessibleName(u" "_s);
if (QLayout *l = mDirectoryServices->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
groupBoxLayout->addWidget(mDirectoryServices);
connect(mDirectoryServices, &DirectoryServicesWidget::changed, q, &DirectoryServicesConfigurationPage::changed);
glay->addWidget(groupBox, row, 0, 1, 3);
}
// LDAP timeout
++row;
mTimeout.createWidgets(q);
mTimeout.label()->setText(i18n("LDAP &timeout (minutes:seconds):"));
mTimeout.widget()->setDisplayFormat(QStringLiteral("mm:ss"));
connect(mTimeout.widget(), &QTimeEdit::timeChanged, q, &DirectoryServicesConfigurationPage::changed);
glay->addWidget(mTimeout.label(), row, 0);
glay->addWidget(mTimeout.widget(), row, 1);
// Max number of items returned by queries
++row;
mMaxItems.createWidgets(q);
mMaxItems.label()->setText(i18n("&Maximum number of items returned by query:"));
mMaxItems.widget()->setMinimum(0);
connect(mMaxItems.widget(), &QSpinBox::valueChanged, q, &DirectoryServicesConfigurationPage::changed);
glay->addWidget(mMaxItems.label(), row, 0);
glay->addWidget(mMaxItems.widget(), row, 1);
++row;
mFetchMissingSignerKeysCB = new QCheckBox{q};
mFetchMissingSignerKeysCB->setText(i18nc("@option:check", "Retrieve missing certification keys when importing new keys"));
mFetchMissingSignerKeysCB->setToolTip(xi18nc("@info:tooltip",
"If enabled, then Kleopatra will automatically try to retrieve the keys "
"that were used to certify the user IDs of newly imported OpenPGP keys."));
connect(mFetchMissingSignerKeysCB, &QCheckBox::toggled, q, &DirectoryServicesConfigurationPage::changed);
glay->addWidget(mFetchMissingSignerKeysCB, row, 0, 1, 3);
++row;
mQueryWKDsForAllUserIDsCB = new QCheckBox{q};
mQueryWKDsForAllUserIDsCB->setText(i18nc("@option:check", "Query certificate directories of providers for all user IDs"));
mQueryWKDsForAllUserIDsCB->setToolTip(xi18nc("@info:tooltip",
"By default, Kleopatra only queries the certificate directories of providers (WKD) "
"for user IDs that were originally retrieved from a WKD when you update an OpenPGP "
"certificate. If this option is enabled, then Kleopatra will query WKDs for all user IDs."));
connect(mQueryWKDsForAllUserIDsCB, &QCheckBox::toggled, q, &DirectoryServicesConfigurationPage::changed);
glay->addWidget(mQueryWKDsForAllUserIDsCB, row, 0, 1, 3);
glay->setRowStretch(++row, 1);
glay->setColumnStretch(2, 1);
}
static auto readKeyserverConfigs(const CryptoConfigEntry *configEntry)
{
std::vector<KeyserverConfig> servers;
if (configEntry) {
const auto urls = configEntry->urlValueList();
+ std::vector<KeyserverConfig> tempServers;
+ tempServers.reserve(urls.size());
+ std::ranges::transform(urls, std::back_inserter(tempServers), &KeyserverConfig::fromUrl);
servers.reserve(urls.size());
- std::transform(std::begin(urls), std::end(urls), std::back_inserter(servers), &KeyserverConfig::fromUrl);
+ // ignore duplicate server entries (workaround for T7828)
+ std::ranges::copy_if(tempServers, std::back_inserter(servers), [&serversRef = std::as_const(servers)](const auto &server) {
+ return !Kleo::contains(serversRef, server);
+ });
}
return servers;
}
void DirectoryServicesConfigurationPage::Private::load(const Kleo::Settings &settings)
{
if (mDirectoryServices) {
mDirectoryServices->clear();
// gpgsm uses the deprecated keyserver option in gpgsm.conf additionally to the ldapserver option in dirmngr.conf;
// we (try to) read servers from both entries, but always write to the newest existing entry
const auto *const newEntry =
configEntry(s_x509services_componentName, s_x509services_entryName, CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoNotShowError);
const auto *const legacyEntry =
configEntry(s_x509services_legacy_componentName, s_x509services_legacy_entryName, CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoNotShowError);
auto entry = newEntry ? newEntry : legacyEntry;
if (entry) {
const auto additionalServers = readKeyserverConfigs(legacyEntry);
auto servers = readKeyserverConfigs(newEntry);
- std::copy(std::begin(additionalServers), std::end(additionalServers), std::back_inserter(servers));
+ // ignore duplicate server entries (workaround for T7828)
+ std::ranges::copy_if(additionalServers, std::back_inserter(servers), [&serversRef = std::as_const(servers)](const auto &server) {
+ return !Kleo::contains(serversRef, server);
+ });
mDirectoryServices->setKeyservers(servers);
mDirectoryServices->setReadOnly(entry->isReadOnly());
} else {
qCWarning(KLEOPATRA_LOG) << "Unknown or wrong typed config entries" << s_x509services_componentName << "/" << s_x509services_entryName << "and"
<< s_x509services_legacy_componentName << "/" << s_x509services_legacy_entryName;
mDirectoryServices->setDisabled(true);
}
mRetrieveKeysCheckBox->setChecked(mRetrieveKeysEntry->boolValue());
}
{
// gpg prefers the deprecated keyserver option in gpg.conf over the keyserver option in dirmngr.conf;
// therefore, we use the deprecated keyserver option if it is set or if the new option doesn't exist (gpg < 2.1.9)
auto const newEntry = configEntry(s_pgpservice_componentName, s_pgpservice_entryName, CryptoConfigEntry::ArgType_String, SingleValue, DoNotShowError);
auto const legacyEntry =
configEntry(s_pgpservice_legacy_componentName, s_pgpservice_legacy_entryName, CryptoConfigEntry::ArgType_String, SingleValue, DoNotShowError);
mOpenPGPServiceEntry = ((legacyEntry && legacyEntry->isSet()) || !newEntry) ? legacyEntry : newEntry;
if (!mOpenPGPServiceEntry) {
qCWarning(KLEOPATRA_LOG) << "Unknown or wrong typed config entries" << s_pgpservice_componentName << "/" << s_pgpservice_entryName << "and"
<< s_pgpservice_legacy_componentName << "/" << s_pgpservice_legacy_entryName;
} else if (mOpenPGPServiceEntry == legacyEntry) {
qCDebug(KLEOPATRA_LOG) << "Using config entry" << s_pgpservice_legacy_componentName << "/" << s_pgpservice_legacy_entryName;
} else {
qCDebug(KLEOPATRA_LOG) << "Using config entry" << s_pgpservice_componentName << "/" << s_pgpservice_entryName;
}
const QString keyserver =
(mOpenPGPServiceEntry && mOpenPGPServiceEntry->isSet()) ? canonicalizedKeyserver(mOpenPGPServiceEntry->stringValue()) : QString();
const QString effectiveServer = effectiveKeyserver(keyserver, mDefaultKeyserver);
mUseKeyServerCheckBox->setChecked(!effectiveServer.isEmpty());
mUseKeyServerCheckBox->setEnabled(mOpenPGPServiceEntry && !mOpenPGPServiceEntry->isReadOnly());
mOpenPGPKeyserverEdit.widget()->setText(displayKeyserver(keyserver));
mOpenPGPKeyserverEdit.setEnabled(mOpenPGPServiceEntry && !mOpenPGPServiceEntry->isReadOnly() && mUseKeyServerCheckBox->isChecked());
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(mUseKeyServerCheckBox->isChecked() ? displayKeyserver(mDefaultKeyserver) : QString());
}
// read LDAP timeout
// first try to read the config entry as int (GnuPG 2.3)
mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_entryName, CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
if (!mTimeoutConfigEntry) {
// if this fails, then try to read the config entry as unsigned int (GnuPG <= 2.2)
mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_entryName, CryptoConfigEntry::ArgType_UInt, SingleValue, DoShowError);
}
if (mTimeoutConfigEntry) {
const int ldapTimeout = mTimeoutConfigEntry->argType() == CryptoConfigEntry::ArgType_Int ? mTimeoutConfigEntry->intValue()
: static_cast<int>(mTimeoutConfigEntry->uintValue());
const QTime time = QTime(0, 0, 0, 0).addSecs(ldapTimeout);
// qCDebug(KLEOPATRA_LOG) <<"timeout:" << mTimeoutConfigEntry->uintValue() <<" ->" << time;
mTimeout.widget()->setTime(time);
}
mTimeout.setEnabled(mTimeoutConfigEntry && !mTimeoutConfigEntry->isReadOnly());
// read max-replies config entry
// first try to read the config entry as int (GnuPG 2.3)
mMaxItemsConfigEntry = configEntry(s_maxitems_componentName, s_maxitems_entryName, CryptoConfigEntry::ArgType_Int, SingleValue, DoNotShowError);
if (!mMaxItemsConfigEntry) {
// if this fails, then try to read the config entry as unsigned int (GnuPG <= 2.2)
mMaxItemsConfigEntry = configEntry(s_maxitems_componentName, s_maxitems_entryName, CryptoConfigEntry::ArgType_UInt, SingleValue, DoShowError);
}
if (mMaxItemsConfigEntry) {
const int value = mMaxItemsConfigEntry->argType() == CryptoConfigEntry::ArgType_Int ? mMaxItemsConfigEntry->intValue()
: static_cast<int>(mMaxItemsConfigEntry->uintValue());
mMaxItems.widget()->blockSignals(true); // KNumInput emits valueChanged from setValue!
mMaxItems.widget()->setValue(value);
mMaxItems.widget()->blockSignals(false);
}
mMaxItems.setEnabled(mMaxItemsConfigEntry && !mMaxItemsConfigEntry->isReadOnly());
mFetchMissingSignerKeysCB->setChecked(settings.retrieveSignerKeysAfterImport());
mFetchMissingSignerKeysCB->setEnabled(!settings.isImmutable(QStringLiteral("RetrieveSignerKeysAfterImport")));
mQueryWKDsForAllUserIDsCB->setChecked(settings.queryWKDsForAllUserIDs());
mQueryWKDsForAllUserIDsCB->setEnabled(!settings.isImmutable(QStringLiteral("QueryWKDsForAllUserIDs")));
}
void DirectoryServicesConfigurationPage::Private::load()
{
load(Settings{});
}
namespace
{
void updateIntegerConfigEntry(QGpgME::CryptoConfigEntry *configEntry, int value)
{
if (!configEntry) {
return;
}
if (configEntry->argType() == CryptoConfigEntry::ArgType_Int) {
if (configEntry->intValue() != value) {
configEntry->setIntValue(value);
}
} else {
const auto newValue = static_cast<unsigned>(value);
if (configEntry->uintValue() != newValue) {
configEntry->setUIntValue(newValue);
}
}
}
}
void DirectoryServicesConfigurationPage::Private::setX509ServerEntry(const std::vector<KeyserverConfig> &servers)
{
const auto newEntry = configEntry(s_x509services_componentName, s_x509services_entryName, CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoNotShowError);
const auto legacyEntry =
configEntry(s_x509services_legacy_componentName, s_x509services_legacy_entryName, CryptoConfigEntry::ArgType_LDAPURL, ListValue, DoNotShowError);
if ((newEntry && newEntry->isReadOnly()) || (legacyEntry && legacyEntry->isReadOnly())) {
// do not change the config entries if either config entry is read-only
return;
}
QList<QUrl> urls;
urls.reserve(servers.size());
std::transform(std::begin(servers), std::end(servers), std::back_inserter(urls), std::mem_fn(&KeyserverConfig::toUrl));
if (newEntry) {
// write all servers to the new config entry
newEntry->setURLValueList(urls);
// and clear the legacy config entry
if (legacyEntry) {
legacyEntry->setURLValueList({});
}
} else if (legacyEntry) {
// write all servers to the legacy config entry if the new entry is not available
legacyEntry->setURLValueList(urls);
} else {
qCWarning(KLEOPATRA_LOG) << "Could not store the X.509 servers. Unknown or wrong typed config entries" << s_x509services_componentName << "/"
<< s_x509services_entryName << "and" << s_x509services_legacy_componentName << "/" << s_x509services_legacy_entryName;
}
}
void DirectoryServicesConfigurationPage::Private::save()
{
if (mDirectoryServices && mDirectoryServices->isEnabled()) {
setX509ServerEntry(mDirectoryServices->keyservers());
}
if (mOpenPGPServiceEntry) {
if (!mUseKeyServerCheckBox->isChecked()) {
mOpenPGPServiceEntry->setStringValue(u"none"_s);
} else {
const auto keyserver = mOpenPGPKeyserverEdit.widget()->text().trimmed();
if (keyserver.isEmpty()) {
mOpenPGPServiceEntry->resetToDefault();
} else if (keyserver == QLatin1StringView{"none"}) {
mOpenPGPServiceEntry->setStringValue(keyserver);
} else {
const auto keyserverValue = keyserver.contains(QLatin1Char{':'}) ? keyserver : (QLatin1StringView{"hkps://"} + keyserver);
mOpenPGPServiceEntry->setStringValue(keyserverValue);
}
}
mRetrieveKeysEntry->setBoolValue(mRetrieveKeysCheckBox->isChecked());
}
const QTime time{mTimeout.widget()->time()};
updateIntegerConfigEntry(mTimeoutConfigEntry, time.minute() * 60 + time.second());
updateIntegerConfigEntry(mMaxItemsConfigEntry, mMaxItems.widget()->value());
mConfig->sync(true);
Settings settings;
settings.setRetrieveSignerKeysAfterImport(mFetchMissingSignerKeysCB->isChecked());
settings.setQueryWKDsForAllUserIDs(mQueryWKDsForAllUserIDsCB->isChecked());
settings.save();
}
void DirectoryServicesConfigurationPage::Private::defaults()
{
// these guys don't have a default, to clear them:
if (mDirectoryServices && mDirectoryServices->isEnabled()) {
setX509ServerEntry({});
}
if (mOpenPGPServiceEntry && !mOpenPGPServiceEntry->isReadOnly()) {
mOpenPGPServiceEntry->setStringValue(QString());
}
// these presumably have a default, use that one:
if (mTimeoutConfigEntry && !mTimeoutConfigEntry->isReadOnly()) {
mTimeoutConfigEntry->resetToDefault();
}
if (mMaxItemsConfigEntry && !mMaxItemsConfigEntry->isReadOnly()) {
mMaxItemsConfigEntry->resetToDefault();
}
if (mRetrieveKeysEntry && !mRetrieveKeysEntry->isReadOnly()) {
mRetrieveKeysEntry->resetToDefault();
}
Settings settings;
settings.setRetrieveSignerKeysAfterImport(settings.findItem(QStringLiteral("RetrieveSignerKeysAfterImport"))->getDefault().toBool());
settings.setQueryWKDsForAllUserIDs(settings.findItem(QStringLiteral("QueryWKDsForAllUserIDs"))->getDefault().toBool());
load(settings);
}
// Find config entry for ldap servers. Implements runtime checks on the configuration option.
CryptoConfigEntry *DirectoryServicesConfigurationPage::Private::configEntry(const char *componentName,
const char *entryName,
CryptoConfigEntry::ArgType argType,
EntryMultiplicity multiplicity,
ShowError showError)
{
CryptoConfigEntry *const entry = Kleo::getCryptoConfigEntry(mConfig, componentName, entryName);
if (!entry) {
if (showError == DoShowError) {
KMessageBox::error(
q,
i18n("Backend error: gpgconf does not seem to know the entry for %1/%2", QLatin1StringView(componentName), QLatin1StringView(entryName)));
}
return nullptr;
}
if (entry->argType() != argType || entry->isList() != bool(multiplicity)) {
if (showError == DoShowError) {
KMessageBox::error(q,
i18n("Backend error: gpgconf has wrong type for %1/%2: %3 %4",
QLatin1StringView(componentName),
QLatin1StringView(entryName),
entry->argType(),
entry->isList()));
}
return nullptr;
}
return entry;
}
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QWidget *parent)
: KleoConfigModule(parent)
, d{new Private{this}}
{
}
DirectoryServicesConfigurationPage::~DirectoryServicesConfigurationPage() = default;
void DirectoryServicesConfigurationPage::load()
{
d->load();
}
void DirectoryServicesConfigurationPage::save()
{
d->save();
}
void DirectoryServicesConfigurationPage::defaults()
{
d->defaults();
}
#include "moc_dirservconfigpage.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:26 PM (11 h, 44 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
24/bb/c75dd1629c77569c1391d7a3f23f

Event Timeline