Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/refreshcertificatecommand.cpp b/src/commands/refreshcertificatecommand.cpp
index 19bb4f758..4c8e18399 100644
--- a/src/commands/refreshcertificatecommand.cpp
+++ b/src/commands/refreshcertificatecommand.cpp
@@ -1,408 +1,414 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/refreshcertificatecommand.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 "command_p.h"
#include "refreshcertificatecommand.h"
+#include <settings.h>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KMessageBox>
#include <QGpgME/Protocol>
#if QGPGME_SUPPORTS_KEY_REFRESH
#include <QGpgME/ReceiveKeysJob>
#include <QGpgME/RefreshKeysJob>
#endif
#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
#include <QGpgME/WKDRefreshJob>
#endif
#include <gpgme++/importresult.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace GpgME;
class RefreshCertificateCommand::Private : public Command::Private
{
friend class ::RefreshCertificateCommand;
RefreshCertificateCommand *q_func() const
{
return static_cast<RefreshCertificateCommand *>(q);
}
public:
explicit Private(RefreshCertificateCommand *qq);
~Private() override;
void start();
void cancel();
#if QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::ReceiveKeysJob> startReceiveKeysJob();
std::unique_ptr<QGpgME::RefreshKeysJob> startSMIMEJob();
#endif
#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
std::unique_ptr<QGpgME::WKDRefreshJob> startWKDRefreshJob();
#endif
void onReceiveKeysJobResult(const ImportResult &result);
void onWKDRefreshJobResult(const ImportResult &result);
void onSMIMEJobResult(const Error &err);
void showOpenPGPResult();
void showError(const Error &err);
private:
Key key;
#if QGPGME_SUPPORTS_KEY_REFRESH
QPointer<QGpgME::Job> job;
#endif
ImportResult receiveKeysResult;
ImportResult wkdRefreshResult;
};
RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
RefreshCertificateCommand::Private::Private(RefreshCertificateCommand *qq)
: Command::Private{qq}
{
}
RefreshCertificateCommand::Private::~Private() = default;
namespace
{
Key getKey(const std::vector<Key> &keys)
{
if (keys.size() != 1) {
qCWarning(KLEOPATRA_LOG) << "Expected exactly one key, but got" << keys.size();
return {};
}
const Key key = keys.front();
if (key.protocol() == GpgME::UnknownProtocol) {
qCWarning(KLEOPATRA_LOG) << "Key has unknown protocol";
return {};
}
return key;
}
}
void RefreshCertificateCommand::Private::start()
{
key = getKey(keys());
if (key.isNull()) {
finished();
return;
}
#if QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::Job> refreshJob;
switch (key.protocol()) {
case GpgME::OpenPGP:
refreshJob = startReceiveKeysJob();
break;
case GpgME::CMS:
refreshJob = startSMIMEJob();
break;
default:; // cannot happen ;-)
}
if (!refreshJob) {
finished();
return;
}
job = refreshJob.release();
#else
error(i18n("The backend does not support updating individual certificates."));
finished();
#endif
}
void RefreshCertificateCommand::Private::cancel()
{
#if QGPGME_SUPPORTS_KEY_REFRESH
if (job) {
job->slotCancel();
}
job.clear();
#endif
}
#if QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::ReceiveKeysJob> RefreshCertificateCommand::Private::startReceiveKeysJob()
{
std::unique_ptr<QGpgME::ReceiveKeysJob> refreshJob{QGpgME::openpgp()->receiveKeysJob()};
Q_ASSERT(refreshJob);
connect(refreshJob.get(), &QGpgME::ReceiveKeysJob::result, q, [this](const GpgME::ImportResult &result) {
onReceiveKeysJobResult(result);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(refreshJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(refreshJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
const GpgME::Error err = refreshJob->start({QString::fromLatin1(key.primaryFingerprint())});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Updating key..."));
return refreshJob;
}
std::unique_ptr<QGpgME::RefreshKeysJob> RefreshCertificateCommand::Private::startSMIMEJob()
{
std::unique_ptr<QGpgME::RefreshKeysJob> refreshJob{QGpgME::smime()->refreshKeysJob()};
Q_ASSERT(refreshJob);
connect(refreshJob.get(), &QGpgME::RefreshKeysJob::result, q, [this](const GpgME::Error &err) {
onSMIMEJobResult(err);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(refreshJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(refreshJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
const GpgME::Error err = refreshJob->start({key});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Updating certificate..."));
return refreshJob;
}
#endif
#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
std::unique_ptr<QGpgME::WKDRefreshJob> RefreshCertificateCommand::Private::startWKDRefreshJob()
{
// check if key is eligible for WKD refresh, i.e. if any user ID has WKD as origin
const auto userIds = key.userIDs();
const auto eligibleForWKDRefresh = std::any_of(userIds.begin(), userIds.end(), [](const auto &userId) {
return !userId.isRevoked() && !userId.addrSpec().empty() && userId.origin() == Key::OriginWKD;
});
if (!eligibleForWKDRefresh) {
wkdRefreshResult = ImportResult{Error::fromCode(GPG_ERR_USER_1)};
return {};
}
std::unique_ptr<QGpgME::WKDRefreshJob> refreshJob{QGpgME::openpgp()->wkdRefreshJob()};
Q_ASSERT(refreshJob);
connect(refreshJob.get(), &QGpgME::WKDRefreshJob::result, q, [this](const GpgME::ImportResult &result) {
onWKDRefreshJobResult(result);
});
connect(refreshJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
- const GpgME::Error err = refreshJob->start({key});
+ Error err;
+ if (Settings{}.queryWKDsForAllUserIDs()) {
+ err = refreshJob->start(key.userIDs());
+ } else {
+ err = refreshJob->start({key});
+ }
if (err) {
wkdRefreshResult = ImportResult{err};
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Updating key..."));
return refreshJob;
}
#endif
namespace
{
static auto informationOnChanges(const ImportResult &result)
{
QString text;
// if additional keys have been retrieved via WKD, then most of the below
// details are just a guess and may concern the additional keys instead of
// the refresh keys; this could only be clarified by a thorough comparison of
// unrefreshed and refreshed key
if (result.numUnchanged() == result.numConsidered()) {
// if numUnchanged < numConsidered, then it is not clear whether the refreshed key
// hasn't changed or whether another key retrieved via WKD hasn't changed
text = i18n("The key hasn't changed.");
} else if (result.newRevocations() > 0) {
// it is possible that a revoked key has been newly imported via WKD,
// but it is much more likely that the refreshed key was revoked
text = i18n("The key has been revoked.");
} else {
// it doesn't make much sense to list below details if the key has been revoked
text = i18n("The key has been updated.");
QStringList details;
if (result.newUserIDs() > 0) {
details.push_back(i18n("New user IDs: %1", result.newUserIDs()));
}
if (result.newSubkeys() > 0) {
details.push_back(i18n("New subkeys: %1", result.newSubkeys()));
}
if (result.newSignatures() > 0) {
details.push_back(i18n("New signatures: %1", result.newSignatures()));
}
if (!details.empty()) {
text += QLatin1String{"<br><br>"} + details.join(QLatin1String{"<br>"});
}
}
text = QLatin1String{"<p>"} + text + QLatin1String{"</p>"};
if (result.numImported() > 0) {
text += QLatin1String{"<p>"}
+ i18np("Additionally, one new key has been retrieved.", "Additionally, %1 new keys have been retrieved.", result.numImported())
+ QLatin1String{"</p>"};
}
return text;
}
}
void RefreshCertificateCommand::Private::onReceiveKeysJobResult(const ImportResult &result)
{
receiveKeysResult = result;
if (result.error().isCanceled()) {
finished();
return;
}
#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
std::unique_ptr<QGpgME::Job> refreshJob = startWKDRefreshJob();
if (!refreshJob) {
showOpenPGPResult();
return;
}
job = refreshJob.release();
#else
if (result.error()) {
showError(result.error());
} else {
information(informationOnChanges(result), i18nc("@title:window", "Key Updated"));
}
finished();
#endif
}
void RefreshCertificateCommand::Private::onWKDRefreshJobResult(const ImportResult &result)
{
wkdRefreshResult = result;
showOpenPGPResult();
}
void RefreshCertificateCommand::Private::onSMIMEJobResult(const Error &err)
{
if (err) {
showError(err);
finished();
return;
}
if (!err.isCanceled()) {
information(i18nc("@info", "The certificate has been updated."), i18nc("@title:window", "Certificate Updated"));
}
finished();
}
void RefreshCertificateCommand::Private::showOpenPGPResult()
{
if (wkdRefreshResult.error().code() == GPG_ERR_USER_1 || wkdRefreshResult.error().isCanceled()) {
if (receiveKeysResult.error()) {
showError(receiveKeysResult.error());
} else {
information(informationOnChanges(receiveKeysResult), i18nc("@title:window", "Key Updated"));
}
finished();
return;
}
if (receiveKeysResult.error() && wkdRefreshResult.error()) {
error(xi18nc("@info",
"<para>Updating the certificate from a keyserver, an LDAP server, or Active Directory failed:</para>"
"<para><message>%1</message></para>"
"<para>Updating the certificate via Web Key Directory failed:</para>"
"<para><message>%2</message></para>",
Formatting::errorAsString(receiveKeysResult.error()),
Formatting::errorAsString(wkdRefreshResult.error())),
i18nc("@title:window", "Update Failed"));
finished();
return;
}
QString text;
text += QLatin1String{"<p><strong>"} + i18nc("@info", "Result of update from keyserver, LDAP server, or Active Directory") + QLatin1String{"</strong></p>"};
if (receiveKeysResult.error()) {
text += xi18nc("@info", "<para>The update failed: <message>%1</message></para>", Formatting::errorAsString(receiveKeysResult.error()));
} else {
text += informationOnChanges(receiveKeysResult);
}
text += QLatin1String{"<p><strong>"} + i18nc("@info", "Result of update via Web Key Directory") + QLatin1String{"</strong></p>"};
if (wkdRefreshResult.error()) {
text += xi18nc("@info", "<para>The update failed: <message>%1</message></para>", Formatting::errorAsString(wkdRefreshResult.error()));
} else {
text += informationOnChanges(wkdRefreshResult);
}
information(text, i18nc("@title:window", "Key Updated"));
finished();
}
void RefreshCertificateCommand::Private::showError(const Error &err)
{
error(xi18nc("@info",
"<para>An error occurred while updating the certificate:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)),
i18nc("@title:window", "Update Failed"));
}
RefreshCertificateCommand::RefreshCertificateCommand(const GpgME::Key &key)
: Command{key, new Private{this}}
{
}
RefreshCertificateCommand::~RefreshCertificateCommand() = default;
void RefreshCertificateCommand::doStart()
{
d->start();
}
void RefreshCertificateCommand::doCancel()
{
d->cancel();
}
#undef d
#undef q
#include "moc_refreshcertificatecommand.cpp"
diff --git a/src/conf/dirservconfigpage.cpp b/src/conf/dirservconfigpage.cpp
index 8da7c0a79..f95d168d2 100644
--- a/src/conf/dirservconfigpage.cpp
+++ b/src/conf/dirservconfigpage.cpp
@@ -1,513 +1,526 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/dirservconfigpage.cpp
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/Compat>
#include <Libkleo/DirectoryServicesWidget>
#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;
// 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;
QGpgME::CryptoConfigEntry *mOpenPGPServiceEntry = nullptr;
QGpgME::CryptoConfigEntry *mTimeoutConfigEntry = nullptr;
QGpgME::CryptoConfigEntry *mMaxItemsConfigEntry = nullptr;
QGpgME::CryptoConfig *mConfig = nullptr;
};
DirectoryServicesConfigurationPage::Private::Private(DirectoryServicesConfigurationPage *q)
{
mConfig = QGpgME::cryptoConfig();
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
auto glay = new QGridLayout(q);
#else
auto glay = new QGridLayout(q->widget());
#endif
glay->setContentsMargins(0, 0, 0, 0);
// OpenPGP keyserver
int row = 0;
{
auto l = new QHBoxLayout{};
l->setContentsMargins(0, 0, 0, 0);
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
mOpenPGPKeyserverEdit.createWidgets(q);
#else
mOpenPGPKeyserverEdit.createWidgets(q->widget());
#endif
mOpenPGPKeyserverEdit.label()->setText(i18n("OpenPGP keyserver:"));
l->addWidget(mOpenPGPKeyserverEdit.label());
l->addWidget(mOpenPGPKeyserverEdit.widget());
glay->addLayout(l, row, 0, 1, 3);
connect(mOpenPGPKeyserverEdit.widget(), &QLineEdit::textEdited, q, &DirectoryServicesConfigurationPage::markAsChanged);
}
// X.509 servers
if (Settings{}.cmsEnabled()) {
++row;
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
auto groupBox = new QGroupBox{i18n("X.509 Directory Services"), q};
#else
auto groupBox = new QGroupBox{i18n("X.509 Directory Services"), q->widget()};
#endif
auto groupBoxLayout = new QVBoxLayout{groupBox};
if (gpgme_check_version("1.16.0")) {
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
mDirectoryServices = new Kleo::DirectoryServicesWidget(q);
#else
mDirectoryServices = new Kleo::DirectoryServicesWidget(q->widget());
#endif
if (QLayout *l = mDirectoryServices->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
groupBoxLayout->addWidget(mDirectoryServices);
connect(mDirectoryServices, &DirectoryServicesWidget::changed, q, &DirectoryServicesConfigurationPage::markAsChanged);
} else {
// QGpgME does not properly support keyserver flags for X.509 keyservers (added in GnuPG 2.2.28);
// disable the configuration to prevent the configuration from being corrupted
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
groupBoxLayout->addWidget(new QLabel{i18n("Configuration of directory services is not possible "
"because the used gpgme libraries are too old."),
q});
#else
groupBoxLayout->addWidget(new QLabel{i18n("Configuration of directory services is not possible "
"because the used gpgme libraries are too old."),
q->widget()});
#endif
}
glay->addWidget(groupBox, row, 0, 1, 3);
}
// LDAP timeout
++row;
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
mTimeout.createWidgets(q);
#else
mTimeout.createWidgets(q->widget());
#endif
mTimeout.label()->setText(i18n("LDAP &timeout (minutes:seconds):"));
mTimeout.widget()->setDisplayFormat(QStringLiteral("mm:ss"));
connect(mTimeout.widget(), &QTimeEdit::timeChanged, q, &DirectoryServicesConfigurationPage::markAsChanged);
glay->addWidget(mTimeout.label(), row, 0);
glay->addWidget(mTimeout.widget(), row, 1);
// Max number of items returned by queries
++row;
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
mMaxItems.createWidgets(q);
#else
mMaxItems.createWidgets(q->widget());
#endif
mMaxItems.label()->setText(i18n("&Maximum number of items returned by query:"));
mMaxItems.widget()->setMinimum(0);
#if QT_DEPRECATED_SINCE(5, 14)
connect(mMaxItems.widget(), qOverload<int>(&QSpinBox::valueChanged), q, &DirectoryServicesConfigurationPage::markAsChanged);
#else
connect(mMaxItems.widget(), &QSpinBox::valueChanged, q, &DirectoryServicesConfigurationPage::markAsChanged);
#endif
glay->addWidget(mMaxItems.label(), row, 0);
glay->addWidget(mMaxItems.widget(), row, 1);
#if QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID
++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::markAsChanged);
glay->addWidget(mFetchMissingSignerKeysCB, row, 0, 1, 3);
#endif
+ ++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::markAsChanged);
+ 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();
servers.reserve(urls.size());
std::transform(std::begin(urls), std::end(urls), std::back_inserter(servers), &KeyserverConfig::fromUrl);
}
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));
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);
}
}
{
// 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;
}
mOpenPGPKeyserverEdit.widget()->setText(mOpenPGPServiceEntry && mOpenPGPServiceEntry->isSet() ? mOpenPGPServiceEntry->stringValue() : QString());
mOpenPGPKeyserverEdit.setEnabled(mOpenPGPServiceEntry && !mOpenPGPServiceEntry->isReadOnly());
#if QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE
if (newEntry && !newEntry->defaultValue().isNull()) {
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(newEntry->defaultValue().toString());
} else
#endif
{
if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.16") {
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(QStringLiteral("hkp://keys.gnupg.net"));
} else {
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(QStringLiteral("hkps://hkps.pool.sks-keyservers.net"));
}
}
}
// 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());
#if QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID
mFetchMissingSignerKeysCB->setChecked(settings.retrieveSignerKeysAfterImport());
mFetchMissingSignerKeysCB->setEnabled(!settings.isImmutable(QStringLiteral("RetrieveSignerKeysAfterImport")));
-#else
- Q_UNUSED(settings)
#endif
+ 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) {
const auto keyserver = mOpenPGPKeyserverEdit.widget()->text().trimmed();
if (keyserver.isEmpty()) {
mOpenPGPServiceEntry->resetToDefault();
} else {
const auto keyserverUrl = keyserver.contains(QLatin1String{"://"}) ? keyserver : (QLatin1String{"hkps://"} + keyserver);
mOpenPGPServiceEntry->setStringValue(keyserverUrl);
}
}
const QTime time{mTimeout.widget()->time()};
updateIntegerConfigEntry(mTimeoutConfigEntry, time.minute() * 60 + time.second());
updateIntegerConfigEntry(mMaxItemsConfigEntry, mMaxItems.widget()->value());
mConfig->sync(true);
-#if QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID
Settings settings;
+#if QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID
settings.setRetrieveSignerKeysAfterImport(mFetchMissingSignerKeysCB->isChecked());
- settings.save();
#endif
+ 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();
}
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) {
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
KMessageBox::error(
q,
i18n("Backend error: gpgconf does not seem to know the entry for %1/%2", QLatin1String(componentName), QLatin1String(entryName)));
#else
KMessageBox::error(
q->widget(),
i18n("Backend error: gpgconf does not seem to know the entry for %1/%2", QLatin1String(componentName), QLatin1String(entryName)));
#endif
}
return nullptr;
}
if (entry->argType() != argType || entry->isList() != bool(multiplicity)) {
if (showError == DoShowError) {
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
KMessageBox::error(q,
i18n("Backend error: gpgconf has wrong type for %1/%2: %3 %4",
QLatin1String(componentName),
QLatin1String(entryName),
entry->argType(),
entry->isList()));
#else
KMessageBox::error(q->widget(),
i18n("Backend error: gpgconf has wrong type for %1/%2: %3 %4",
QLatin1String(componentName),
QLatin1String(entryName),
entry->argType(),
entry->isList()));
#endif
}
return nullptr;
}
return entry;
}
#if KCMUTILS_VERSION < QT_VERSION_CHECK(5, 240, 0)
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QWidget *parent, const QVariantList &args)
: KCModule{parent, args}
#else
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: KCModule(parent, data, args)
#endif
, d{new Private{this}}
{
}
DirectoryServicesConfigurationPage::~DirectoryServicesConfigurationPage() = default;
void DirectoryServicesConfigurationPage::load()
{
d->load();
}
void DirectoryServicesConfigurationPage::save()
{
d->save();
}
void DirectoryServicesConfigurationPage::defaults()
{
d->defaults();
}
diff --git a/src/kcfg/settings.kcfg b/src/kcfg/settings.kcfg
index 3192b1a40..ca4712fe3 100644
--- a/src/kcfg/settings.kcfg
+++ b/src/kcfg/settings.kcfg
@@ -1,198 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="kleopatrarc" />
<group name="CertificateCreationWizard">
<entry key="CN_placeholder" name="cnPlaceholder" type="String">
<label>Placeholder for CN</label>
<whatsthis>This text will be used as placeholder text for the common name (CN) field of S/MIME certificates.</whatsthis>
<default></default>
</entry>
<entry key="CN_prefill" name="prefillCN" type="Bool">
<label>Prefill CN automatically</label>
<whatsthis>If true, then the common name (CN) field of S/MIME certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="EMAIL_placeholder" name="emailPlaceholder" type="String">
<label>Placeholder for EMAIL</label>
<whatsthis>This text will be used as placeholder text for the email address field of OpenPGP and S/MIME certificates.</whatsthis>
<default></default>
</entry>
<entry key="EMAIL_prefill" name="prefillEmail" type="Bool">
<label>Prefill EMAIL automatically</label>
<whatsthis>If true, then the email address field of OpenPGP and S/MIME certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="NAME_placeholder" name="namePlaceholder" type="String">
<label>Placeholder for NAME</label>
<whatsthis>This text will be used as placeholder text for the name field of OpenPGP certificates.</whatsthis>
<default></default>
</entry>
<entry key="NAME_prefill" name="prefillName" type="Bool">
<label>Prefill NAME automatically</label>
<whatsthis>If true, then the name field of OpenPGP certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="ValidityPeriodInDays" type="Int">
<label>Default validity period</label>
<tooltip>Specifies the default validity period of new OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies how many days a new OpenPGP key is valid by default, or, in other words, after how many days the key will expire. Set this to 0 for unlimited validity. If this setting is not set or if it is set to a negative value, then new OpenPGP keys will be valid for three years (possibly clamped to the allowed minimum or maximum validity period) by default.</whatsthis>
<default>-1</default>
</entry>
<entry key="ValidityPeriodInDaysMin" type="Int">
<label>Minimum validity period</label>
<tooltip>Specifies the minimum validity period of new OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies how many days a new OpenPGP key is valid at least, or, in other words, after how many days the new key will expire at the earliest.</whatsthis>
<default>1</default>
</entry>
<entry key="ValidityPeriodInDaysMax" type="Int">
<label>Maximum validity period</label>
<tooltip>Specifies the maximum validity period of new OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies how many days a new OpenPGP key is valid at most, or, in other words, after how many days the new key will expire at the latest. If this setting is not set or if it is set to a negative value, then unlimited validity is allowed.</whatsthis>
<default>-1</default>
</entry>
<entry key="HideAdvanced" type="Bool">
<label>Hide advanced settings</label>
<whatsthis>If true, hides the advanced settings button in the new certificate wizard.</whatsthis>
<default>false</default>
</entry>
</group>
<group name="Certification">
<entry key="CertificationValidityInDays" type="Int">
<label>Default certification validity</label>
<tooltip>Specifies the default validity of certifications in days.</tooltip>
<whatsthis>This setting specifies how many days a certification is valid by default, or, in other words, after how many days a new certification will expire. Set this to 0 for unlimited validity of certifications.</whatsthis>
<default>0</default>
</entry>
</group>
<group name="ChecksumOperations">
<entry key="checksum-definition-id" name="ChecksumDefinitionId" type="String">
<label>Checksum program to use when creating checksum files</label>
<default>sha256sum</default>
</entry>
</group>
<group name="Clipboard">
<entry key="ShowResultsAfterSigning" name="ShowResultsAfterSigningClipboard" type="Bool">
<label>Show results after signing</label>
<whatsthis>If true, then the results are shown after successfully signing the clipboard.</whatsthis>
<default>true</default>
</entry>
<entry key="ShowResultsAfterEncryption" name="ShowResultsAfterEncryptingClipboard" type="Bool">
<label>Show results after encryption</label>
<whatsthis>If true, then the results are shown after successfully encrypting the clipboard.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="CMS">
<entry key="Enabled" name="cmsEnabled" type="Bool">
<label>Enable S/MIME</label>
<tooltip>Enables support for S/MIME (CMS).</tooltip>
<whatsthis>If false, then Kleopatra's main UI will not offer any functionality related to S/MIME (CMS).</whatsthis>
<default>true</default>
</entry>
<entry key="AllowCertificateCreation" name="cmsCertificateCreationAllowed" type="Bool">
<label>Allow S/MIME certificate creation</label>
<tooltip>Allows the creation of S/MIME certificate signing requests.</tooltip>
<whatsthis>If false, then Kleopatra will not offer the creation of S/MIME certificate signing requests.</whatsthis>
<default>true</default>
</entry>
<entry key="AllowSigning" name="cmsSigningAllowed" type="Bool">
<label>Allow signing with S/MIME certificates</label>
<tooltip>Allows signing of text or files with S/MIME certificates.</tooltip>
<whatsthis>If false, then Kleopatra will not offer functionality for creating signatures with S/MIME certificates.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="ConfigurationDialog">
<entry name="ShowAppearanceConfiguration" type="Bool">
<label>Show appearance configuration</label>
<default>true</default>
</entry>
<entry name="ShowCryptoOperationsConfiguration" type="Bool">
<label>Show crypto operations configuration</label>
<default>true</default>
</entry>
<entry name="ShowDirectoryServicesConfiguration" type="Bool">
<label>Show directory services configuration</label>
<default>true</default>
</entry>
<entry name="ShowGnuPGSystemConfiguration" type="Bool">
<label>Show GnuPG system configuration</label>
<default>true</default>
</entry>
<entry name="ShowSmartCardsConfiguration" type="Bool">
<label>Show smart cards configuration</label>
<default>true</default>
</entry>
<entry name="ShowSMimeValidationConfiguration" type="Bool">
<label>Show S/MIME validation configuration</label>
<default>true</default>
</entry>
</group>
<group name="DN">
<entry name="AttributeOrder" type="StringList">
<label>DN-Attribute Order</label>
<tooltip>Specifies the display order of the DN attributes of X.509 certificates.</tooltip>
<default></default>
</entry>
</group>
<group name="Groups">
<entry name="GroupsEnabled" type="Bool">
<label>Enable Groups</label>
<tooltip>Enable usage of groups of keys.</tooltip>
<whatsthis>Enable usage of groups of keys to create lists of recipients.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="Import">
<entry name="RetrieveSignerKeysAfterImport" type="Bool">
<label>Retrieve signer keys after import</label>
<whatsthis>If enabled, then Kleopatra will automatically try to retrieve the keys
that were used to certify the user ids of newly imported OpenPGP keys. This is
useful in combination with trusted introducers.</whatsthis>
<default>false</default>
</entry>
+ <entry name="QueryWKDsForAllUserIDs" type="Bool">
+ <label>Query certificate directories of providers for all user IDs</label>
+ <whatsthis>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.</whatsthis>
+ <default>false</default>
+ </entry>
</group>
<group name="Notifications">
<entry name="ShowExpiryNotifications" type="Bool">
<label>Notify about upcoming certificate expiration</label>
<whatsthis>If enabled, then Kleopatra will show notifications in some place when using
certificates that are about to expire soon.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="Privacy">
<entry name="BlockedUrlSchemes" type="StringList">
<label>URL schemes to block</label>
<whatsthis>This is a list of URL schemes that shall be blocked by the application.
This can be used to prevent the application from opening external applications for certain URLs.</whatsthis>
<default></default>
</entry>
</group>
<group name="Smartcard">
<entry name="AlwaysSearchCardOnKeyserver" type="Bool">
<label>Always search smartcard certificates on keyserver</label>
<tooltip>Searches for the certificates belonging the smartcard keys on the configured keyserver.</tooltip>
<whatsthis>Searches on keyservers regardless of the protocol for the smartcards key, regardless
of the keyserver protocol. Default behavior is to only do this for LDAP keyservers.</whatsthis>
<default>false</default>
</entry>
<entry key="AutoLoadP15Certs" name="autoLoadP15Certs" type="Bool">
<label>Try to load S/MIME certificates from PKCS#15 smartcards</label>
<tooltip>Automatically load S/MIME certificates from PKCS#15 (CardOS) smartcards</tooltip>
<whatsthis>If true, then Kleopatra will call gpgsm --learn if a PKCS#15 Smartcard is inserted with unknown certificates. This can take a while and blocks the smartcard while the command is running.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="General">
<entry name="ProfilesDisabled" type="Bool">
<label>Disable profile settings</label>
<default>false</default>
</entry>
</group>
</kcfg>

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 18, 11:18 PM (23 h, 17 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
27/a8/1d34367df53a57cb6beca06f7a13

Event Timeline