Page MenuHome GnuPG

No OneTemporary

diff --git a/src/conf/cryptooperationsconfigwidget.cpp b/src/conf/cryptooperationsconfigwidget.cpp
index 045b689f1..eb43e66d7 100644
--- a/src/conf/cryptooperationsconfigwidget.cpp
+++ b/src/conf/cryptooperationsconfigwidget.cpp
@@ -1,231 +1,224 @@
/*
cryptooperationsconfigwidget.cpp
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 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 "cryptooperationsconfigwidget.h"
#include "kleopatra_debug.h"
#include "fileoperationspreferences.h"
#include "settings.h"
#include <Libkleo/ChecksumDefinition>
#include <Libkleo/KeyFilterManager>
#include <libkleo/classifyconfig.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QCheckBox>
#include <QComboBox>
#include <QDir>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QRegularExpression>
#include <QVBoxLayout>
#include <memory>
using namespace Kleo;
using namespace Kleo::Config;
CryptoOperationsConfigWidget::CryptoOperationsConfigWidget(QWidget *p, Qt::WindowFlags f)
: QWidget{p, f}
{
setupGui();
}
void CryptoOperationsConfigWidget::setupGui()
{
auto baseLay = new QVBoxLayout(this);
- baseLay->setContentsMargins(0, 0, 0, 0);
- auto fileGrp = new QGroupBox(i18n("File Operations"));
- auto fileGrpLay = new QVBoxLayout;
mPGPFileExtCB = new QCheckBox(i18n(R"(Create OpenPGP encrypted files with ".pgp" file extensions instead of ".gpg")"));
mASCIIArmorCB = new QCheckBox(i18n("Create signed or encrypted files as text files."));
mASCIIArmorCB->setToolTip(i18nc("@info",
"Set this option to encode encrypted or signed files as base64 encoded text. "
"So that they can be opened with an editor or sent in a mail body. "
"This will increase file size by one third."));
mTreatP7mEmailCB = new QCheckBox(i18nc("@option:check", "Treat .p7m files without extensions as mails."));
mAutoDecryptVerifyCB = new QCheckBox(i18n("Automatically start operation based on input detection for decrypt/verify."));
mAutoExtractArchivesCB = new QCheckBox(i18n("Automatically extract file archives after decryption"));
mTmpDirCB = new QCheckBox(i18n("Create temporary decrypted files in the folder of the encrypted file."));
mTmpDirCB->setToolTip(i18nc("@info", "Set this option to avoid using the users temporary directory."));
mSymmetricOnlyCB = new QCheckBox(i18n("Use symmetric encryption only."));
mSymmetricOnlyCB->setToolTip(i18nc("@info", "Set this option to disable public key encryption."));
- fileGrpLay->addWidget(mPGPFileExtCB);
- fileGrpLay->addWidget(mTreatP7mEmailCB);
- fileGrpLay->addWidget(mAutoDecryptVerifyCB);
- fileGrpLay->addWidget(mAutoExtractArchivesCB);
- fileGrpLay->addWidget(mASCIIArmorCB);
- fileGrpLay->addWidget(mTmpDirCB);
- fileGrpLay->addWidget(mSymmetricOnlyCB);
+ baseLay->addWidget(mPGPFileExtCB);
+ baseLay->addWidget(mTreatP7mEmailCB);
+ baseLay->addWidget(mAutoDecryptVerifyCB);
+ baseLay->addWidget(mAutoExtractArchivesCB);
+ baseLay->addWidget(mASCIIArmorCB);
+ baseLay->addWidget(mTmpDirCB);
+ baseLay->addWidget(mSymmetricOnlyCB);
auto comboLay = new QGridLayout;
mChecksumDefinitionCB.createWidgets(this);
mChecksumDefinitionCB.label()->setText(i18n("Checksum program to use when creating checksum files:"));
comboLay->addWidget(mChecksumDefinitionCB.label(), 0, 0);
comboLay->addWidget(mChecksumDefinitionCB.widget(), 0, 1);
mArchiveDefinitionCB.createWidgets(this);
mArchiveDefinitionCB.label()->setText(i18n("Archive command to use when archiving files:"));
comboLay->addWidget(mArchiveDefinitionCB.label(), 1, 0);
comboLay->addWidget(mArchiveDefinitionCB.widget(), 1, 1);
- fileGrpLay->addLayout(comboLay);
-
- fileGrp->setLayout(fileGrpLay);
- baseLay->addWidget(fileGrp);
-
+ baseLay->addLayout(comboLay);
baseLay->addStretch(1);
for (auto cb : findChildren<QCheckBox *>()) {
connect(cb, &QCheckBox::toggled, this, &CryptoOperationsConfigWidget::changed);
}
for (auto combo : findChildren<QComboBox *>()) {
connect(combo, &QComboBox::currentIndexChanged, this, &CryptoOperationsConfigWidget::changed);
}
}
CryptoOperationsConfigWidget::~CryptoOperationsConfigWidget()
{
}
void CryptoOperationsConfigWidget::defaults()
{
FileOperationsPreferences filePrefs;
filePrefs.setUsePGPFileExt(filePrefs.findItem(QStringLiteral("UsePGPFileExt"))->getDefault().toBool());
filePrefs.setAutoDecryptVerify(filePrefs.findItem(QStringLiteral("AutoDecryptVerify"))->getDefault().toBool());
filePrefs.setAutoExtractArchives(filePrefs.findItem(QStringLiteral("AutoExtractArchives"))->getDefault().toBool());
filePrefs.setAddASCIIArmor(filePrefs.findItem(QStringLiteral("AddASCIIArmor"))->getDefault().toBool());
filePrefs.setDontUseTmpDir(filePrefs.findItem(QStringLiteral("DontUseTmpDir"))->getDefault().toBool());
filePrefs.setSymmetricEncryptionOnly(filePrefs.findItem(QStringLiteral("SymmetricEncryptionOnly"))->getDefault().toBool());
filePrefs.setArchiveCommand(filePrefs.findItem(QStringLiteral("ArchiveCommand"))->getDefault().toString());
ClassifyConfig classifyConfig;
classifyConfig.setP7mWithoutExtensionAreEmail(classifyConfig.defaultP7mWithoutExtensionAreEmailValue());
Settings settings;
settings.setChecksumDefinitionId(settings.findItem(QStringLiteral("ChecksumDefinitionId"))->getDefault().toString());
load(filePrefs, settings, classifyConfig);
}
void CryptoOperationsConfigWidget::load(const Kleo::FileOperationsPreferences &filePrefs,
const Kleo::Settings &settings,
const Kleo::ClassifyConfig &classifyConfig)
{
mPGPFileExtCB->setChecked(filePrefs.usePGPFileExt());
mPGPFileExtCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("UsePGPFileExt")));
mAutoDecryptVerifyCB->setChecked(filePrefs.autoDecryptVerify());
mAutoDecryptVerifyCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AutoDecryptVerify")));
mAutoExtractArchivesCB->setChecked(filePrefs.autoExtractArchives());
mAutoExtractArchivesCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AutoExtractArchives")));
mASCIIArmorCB->setChecked(filePrefs.addASCIIArmor());
mASCIIArmorCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AddASCIIArmor")));
mTmpDirCB->setChecked(filePrefs.dontUseTmpDir());
mTmpDirCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("DontUseTmpDir")));
mSymmetricOnlyCB->setChecked(filePrefs.symmetricEncryptionOnly());
mSymmetricOnlyCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("SymmetricEncryptionOnly")));
mTreatP7mEmailCB->setEnabled(!classifyConfig.isP7mWithoutExtensionAreEmailImmutable());
const auto defaultChecksumDefinitionId = settings.checksumDefinitionId();
{
const auto index = mChecksumDefinitionCB.widget()->findData(defaultChecksumDefinitionId);
if (index >= 0) {
mChecksumDefinitionCB.widget()->setCurrentIndex(index);
} else {
qCWarning(KLEOPATRA_LOG) << "No checksum definition found with id" << defaultChecksumDefinitionId;
}
}
mChecksumDefinitionCB.setEnabled(!settings.isImmutable(QStringLiteral("ChecksumDefinitionId")));
const auto ad_default_id = filePrefs.archiveCommand();
{
const auto index = mArchiveDefinitionCB.widget()->findData(ad_default_id);
if (index >= 0) {
mArchiveDefinitionCB.widget()->setCurrentIndex(index);
} else {
qCWarning(KLEOPATRA_LOG) << "No archive definition found with id" << ad_default_id;
}
}
mArchiveDefinitionCB.setEnabled(!filePrefs.isImmutable(QStringLiteral("ArchiveCommand")));
}
void CryptoOperationsConfigWidget::load()
{
mChecksumDefinitionCB.widget()->clear();
const auto cds = ChecksumDefinition::getChecksumDefinitions();
for (const std::shared_ptr<ChecksumDefinition> &cd : cds) {
mChecksumDefinitionCB.widget()->addItem(cd->label(), QVariant{cd->id()});
}
// This is a weird hack but because we are a KCM we can't link
// against ArchiveDefinition which pulls in loads of other classes.
// So we do the parsing which archive definitions exist here ourself.
mArchiveDefinitionCB.widget()->clear();
if (KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"))) {
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Archive Definition #")));
for (const QString &group : groups) {
const KConfigGroup cGroup(config, group);
const QString id = cGroup.readEntryUntranslated(QStringLiteral("id"));
const QString name = cGroup.readEntry("Name");
mArchiveDefinitionCB.widget()->addItem(name, QVariant(id));
}
}
load(FileOperationsPreferences{}, Settings{}, ClassifyConfig{});
}
void CryptoOperationsConfigWidget::save()
{
FileOperationsPreferences filePrefs;
filePrefs.setUsePGPFileExt(mPGPFileExtCB->isChecked());
filePrefs.setAutoDecryptVerify(mAutoDecryptVerifyCB->isChecked());
filePrefs.setAutoExtractArchives(mAutoExtractArchivesCB->isChecked());
filePrefs.setAddASCIIArmor(mASCIIArmorCB->isChecked());
filePrefs.setDontUseTmpDir(mTmpDirCB->isChecked());
filePrefs.setSymmetricEncryptionOnly(mSymmetricOnlyCB->isChecked());
Settings settings;
const int idx = mChecksumDefinitionCB.widget()->currentIndex();
if (idx >= 0) {
const auto id = mChecksumDefinitionCB.widget()->itemData(idx).toString();
settings.setChecksumDefinitionId(id);
}
settings.save();
const int aidx = mArchiveDefinitionCB.widget()->currentIndex();
if (aidx >= 0) {
const QString id = mArchiveDefinitionCB.widget()->itemData(aidx).toString();
filePrefs.setArchiveCommand(id);
}
filePrefs.save();
ClassifyConfig classifyConfig;
classifyConfig.setP7mWithoutExtensionAreEmail(mTreatP7mEmailCB->isChecked());
classifyConfig.save();
}
#include "moc_cryptooperationsconfigwidget.cpp"
diff --git a/src/conf/dirservconfigpage.cpp b/src/conf/dirservconfigpage.cpp
index 854a8e4cb..be2f017a0 100644
--- a/src/conf/dirservconfigpage.cpp
+++ b/src/conf/dirservconfigpage.cpp
@@ -1,464 +1,465 @@
/* -*- 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();
auto glay = new QGridLayout(q->widget());
- glay->setContentsMargins(0, 0, 0, 0);
// OpenPGP keyserver
int row = 0;
{
auto l = new QHBoxLayout{};
l->setContentsMargins(0, 0, 0, 0);
mOpenPGPKeyserverEdit.createWidgets(q->widget());
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;
auto groupBox = new QGroupBox{i18n("X.509 Directory Services"), q->widget()};
+ groupBox->setFlat(true);
auto groupBoxLayout = new QVBoxLayout{groupBox};
+ groupBoxLayout->setContentsMargins({});
if (gpgme_check_version("1.16.0")) {
mDirectoryServices = new Kleo::DirectoryServicesWidget(q->widget());
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
groupBoxLayout->addWidget(new QLabel{i18n("Configuration of directory services is not possible "
"because the used gpgme libraries are too old."),
q->widget()});
}
glay->addWidget(groupBox, row, 0, 1, 3);
}
// LDAP timeout
++row;
mTimeout.createWidgets(q->widget());
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;
mMaxItems.createWidgets(q->widget());
mMaxItems.label()->setText(i18n("&Maximum number of items returned by query:"));
mMaxItems.widget()->setMinimum(0);
connect(mMaxItems.widget(), &QSpinBox::valueChanged, q, &DirectoryServicesConfigurationPage::markAsChanged);
glay->addWidget(mMaxItems.label(), row, 0);
glay->addWidget(mMaxItems.widget(), row, 1);
++row;
mFetchMissingSignerKeysCB = new QCheckBox{q->widget()};
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);
++row;
mQueryWKDsForAllUserIDsCB = new QCheckBox{q->widget()};
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 (newEntry && !newEntry->defaultValue().isNull()) {
mOpenPGPKeyserverEdit.widget()->setPlaceholderText(newEntry->defaultValue().toString());
} else {
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());
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) {
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);
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();
}
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->widget(),
i18n("Backend error: gpgconf does not seem to know the entry for %1/%2", QLatin1String(componentName), QLatin1String(entryName)));
}
return nullptr;
}
if (entry->argType() != argType || entry->isList() != bool(multiplicity)) {
if (showError == DoShowError) {
KMessageBox::error(q->widget(),
i18n("Backend error: gpgconf has wrong type for %1/%2: %3 %4",
QLatin1String(componentName),
QLatin1String(entryName),
entry->argType(),
entry->isList()));
}
return nullptr;
}
return entry;
}
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QObject *parent, const KPluginMetaData &data)
: KCModule(parent, data)
, 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"
diff --git a/src/conf/groupsconfigwidget.cpp b/src/conf/groupsconfigwidget.cpp
index b1ee14d05..6fec07dc9 100644
--- a/src/conf/groupsconfigwidget.cpp
+++ b/src/conf/groupsconfigwidget.cpp
@@ -1,457 +1,461 @@
/*
conf/groupsconfigwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "groupsconfigwidget.h"
#include <commands/certifygroupcommand.h>
#include <commands/exportgroupscommand.h>
#include <dialogs/editgroupdialog.h>
#include <Libkleo/Debug>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyHelpers>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <KLocalizedString>
#include <KRandom>
#include <QItemSelectionModel>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QPushButton>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
Q_DECLARE_METATYPE(KeyGroup)
namespace
{
class ListView : public QListView
{
Q_OBJECT
public:
using QListView::QListView;
protected:
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override
{
// workaround bug in QListView::currentChanged which sends an accessible focus event
// even if the list view doesn't have focus
if (hasFocus()) {
QListView::currentChanged(current, previous);
} else {
// skip the reimplementation of currentChanged in QListView
QAbstractItemView::currentChanged(current, previous);
}
}
void focusInEvent(QFocusEvent *event) override
{
QListView::focusInEvent(event);
// select current item if it isn't selected
if (currentIndex().isValid() && !selectionModel()->isSelected(currentIndex())) {
selectionModel()->select(currentIndex(), QItemSelectionModel::ClearAndSelect);
}
}
};
class ProxyModel : public AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent = nullptr)
: AbstractKeyListSortFilterProxyModel(parent)
{
}
~ProxyModel() override = default;
ProxyModel *clone() const override
{
// compiler-generated copy ctor is fine!
return new ProxyModel(*this);
}
int columnCount(const QModelIndex &parent = {}) 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
// list view
return 1;
}
QVariant data(const QModelIndex &idx, int role) const override
{
if (!idx.isValid()) {
return {};
}
return AbstractKeyListSortFilterProxyModel::data(index(idx.row(), KeyList::Summary), role);
}
};
struct Selection {
KeyGroup current;
std::vector<KeyGroup> selected;
};
}
class GroupsConfigWidget::Private
{
friend class ::Kleo::GroupsConfigWidget;
GroupsConfigWidget *const q;
struct {
QLineEdit *groupsFilter = nullptr;
QListView *groupsList = nullptr;
QPushButton *newButton = nullptr;
QPushButton *editButton = nullptr;
QPushButton *deleteButton = nullptr;
QPushButton *certifyButton = nullptr;
QPushButton *exportButton = nullptr;
} ui;
AbstractKeyListModel *groupsModel = nullptr;
ProxyModel *groupsFilterModel = nullptr;
public:
Private(GroupsConfigWidget *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
auto groupsLayout = new QGridLayout;
+ groupsLayout->setContentsMargins(q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
+ q->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
+ q->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
+ q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
groupsLayout->setColumnStretch(0, 1);
groupsLayout->setRowStretch(1, 1);
int row = -1;
row++;
{
auto hbox = new QHBoxLayout;
auto label = new QLabel{i18nc("@label", "Search:")};
label->setAccessibleName(i18nc("@label", "Search groups"));
label->setToolTip(i18nc("@info:tooltip", "Search the list for groups matching the search term."));
hbox->addWidget(label);
ui.groupsFilter = new QLineEdit(q);
ui.groupsFilter->setClearButtonEnabled(true);
ui.groupsFilter->setAccessibleName(i18nc("@label", "Search groups"));
ui.groupsFilter->setToolTip(i18nc("@info:tooltip", "Search the list for groups matching the search term."));
ui.groupsFilter->setPlaceholderText(i18nc("@info::placeholder", "Enter search term"));
ui.groupsFilter->setCursorPosition(0); // prevent emission of accessible text cursor event before accessible focus event
label->setBuddy(ui.groupsFilter);
hbox->addWidget(ui.groupsFilter, 1);
groupsLayout->addLayout(hbox, row, 0);
}
row++;
groupsModel = AbstractKeyListModel::createFlatKeyListModel(q);
groupsFilterModel = new ProxyModel(q);
groupsFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setFilterKeyColumn(KeyList::Summary);
groupsFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setSourceModel(groupsModel);
groupsFilterModel->sort(KeyList::Summary, Qt::AscendingOrder);
ui.groupsList = new ListView(q);
ui.groupsList->setAccessibleName(i18nc("groups of keys", "groups"));
ui.groupsList->setModel(groupsFilterModel);
ui.groupsList->setSelectionBehavior(QAbstractItemView::SelectRows);
ui.groupsList->setSelectionMode(QAbstractItemView::ExtendedSelection);
groupsLayout->addWidget(ui.groupsList, row, 0);
auto groupsButtonLayout = new QVBoxLayout;
ui.newButton = new QPushButton(i18nc("@action:button", "New"), q);
groupsButtonLayout->addWidget(ui.newButton);
ui.editButton = new QPushButton(i18nc("@action:button", "Edit"), q);
ui.editButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.editButton);
ui.deleteButton = new QPushButton(i18nc("@action:button", "Delete"), q);
ui.deleteButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.deleteButton);
ui.certifyButton = new QPushButton{i18nc("@action:button", "Certify"), q};
ui.certifyButton->setToolTip(i18nc("@info:tooltip", "Start the certification process for all certificates in the group."));
ui.certifyButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.certifyButton);
ui.exportButton = new QPushButton{i18nc("@action:button", "Export"), q};
ui.exportButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.exportButton);
groupsButtonLayout->addStretch(1);
groupsLayout->addLayout(groupsButtonLayout, row, 1);
mainLayout->addLayout(groupsLayout, /*stretch=*/1);
connect(ui.groupsFilter, &QLineEdit::textChanged, q, [this](const auto &s) {
groupsFilterModel->setFilterRegularExpression(QRegularExpression::escape(s));
});
connect(ui.groupsList->selectionModel(), &QItemSelectionModel::selectionChanged, q, [this]() {
selectionChanged();
});
connect(ui.groupsList, &QListView::doubleClicked, q, [this](const QModelIndex &index) {
editGroup(index);
});
connect(ui.newButton, &QPushButton::clicked, q, [this]() {
addGroup();
});
connect(ui.editButton, &QPushButton::clicked, q, [this]() {
editGroup();
});
connect(ui.deleteButton, &QPushButton::clicked, q, [this]() {
deleteGroup();
});
connect(ui.certifyButton, &QPushButton::clicked, q, [this]() {
certifyGroup();
});
connect(ui.exportButton, &QPushButton::clicked, q, [this]() {
exportGroup();
});
}
~Private()
{
}
private:
auto getGroupIndex(const KeyGroup &group)
{
QModelIndex index;
if (const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(ui.groupsList->model())) {
index = klmi->index(group);
}
return index;
}
auto selectedRows()
{
return ui.groupsList->selectionModel()->selectedRows();
}
auto getGroup(const QModelIndex &index)
{
return index.isValid() ? ui.groupsList->model()->data(index, KeyList::GroupRole).value<KeyGroup>() : KeyGroup{};
}
auto getGroups(const QModelIndexList &indexes)
{
std::vector<KeyGroup> groups;
std::transform(std::begin(indexes), std::end(indexes), std::back_inserter(groups), [this](const auto &index) {
return getGroup(index);
});
return groups;
}
Selection saveSelection()
{
return {getGroup(ui.groupsList->selectionModel()->currentIndex()), getGroups(selectedRows())};
}
void restoreSelection(const Selection &selection)
{
auto selectionModel = ui.groupsList->selectionModel();
selectionModel->clearSelection();
for (const auto &group : selection.selected) {
selectionModel->select(getGroupIndex(group), QItemSelectionModel::Select | QItemSelectionModel::Rows);
}
auto currentIndex = getGroupIndex(selection.current);
if (currentIndex.isValid()) {
// keep current item if old current group is gone
selectionModel->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
}
}
void selectionChanged()
{
const auto selectedGroups = getGroups(selectedRows());
const bool allSelectedGroupsAreEditable = std::all_of(std::begin(selectedGroups), std::end(selectedGroups), [](const auto &g) {
return !g.isNull() && !g.isImmutable();
});
ui.editButton->setEnabled(selectedGroups.size() == 1 && allSelectedGroupsAreEditable);
ui.deleteButton->setEnabled(!selectedGroups.empty() && allSelectedGroupsAreEditable);
ui.certifyButton->setEnabled(selectedGroups.size() == 1 //
&& !selectedGroups.front().keys().empty() //
&& allKeysHaveProtocol(selectedGroups.front().keys(), GpgME::OpenPGP));
ui.exportButton->setEnabled(selectedGroups.size() == 1);
}
KeyGroup showEditGroupDialog(KeyGroup group, const QString &windowTitle, EditGroupDialog::FocusWidget focusWidget)
{
auto dialog = std::make_unique<EditGroupDialog>(q);
dialog->setWindowTitle(windowTitle);
dialog->setGroupName(group.name());
const KeyGroup::Keys &keys = group.keys();
dialog->setGroupKeys(std::vector<GpgME::Key>(keys.cbegin(), keys.cend()));
dialog->setInitialFocus(focusWidget);
const int result = dialog->exec();
if (result == QDialog::Rejected) {
return KeyGroup();
}
group.setName(dialog->groupName());
group.setKeys(dialog->groupKeys());
return group;
}
void addGroup()
{
const KeyGroup::Id newId = KRandom::randomString(8);
KeyGroup group = KeyGroup(newId, i18nc("default name for new group of keys", "New Group"), {}, KeyGroup::ApplicationConfig);
group.setIsImmutable(false);
const KeyGroup newGroup = showEditGroupDialog(group, i18nc("@title:window a group of keys", "New Group"), EditGroupDialog::GroupName);
if (newGroup.isNull()) {
return;
}
const QModelIndex newIndex = groupsModel->addGroup(newGroup);
if (!newIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "Adding group to model failed";
return;
}
Q_EMIT q->changed();
}
void editGroup(const QModelIndex &index = {})
{
QModelIndex groupIndex;
if (index.isValid()) {
groupIndex = index;
} else {
const auto selection = selectedRows();
if (selection.size() != 1) {
qCDebug(KLEOPATRA_LOG) << (selection.empty() ? "selection is empty" : "more than one group is selected");
return;
}
groupIndex = selection.front();
}
const KeyGroup group = getGroup(groupIndex);
if (group.isNull()) {
qCDebug(KLEOPATRA_LOG) << "selected group is null";
return;
}
if (group.isImmutable()) {
qCDebug(KLEOPATRA_LOG) << "selected group is immutable";
return;
}
const KeyGroup updatedGroup = showEditGroupDialog(group, i18nc("@title:window a group of keys", "Edit Group"), EditGroupDialog::KeysFilter);
if (updatedGroup.isNull()) {
return;
}
// look up index of updated group; the groupIndex used above may have become invalid
const auto updatedGroupIndex = getGroupIndex(updatedGroup);
if (!updatedGroupIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Failed to find index of group" << updatedGroup;
return;
}
const bool success = ui.groupsList->model()->setData(updatedGroupIndex, QVariant::fromValue(updatedGroup));
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Updating group in model failed";
return;
}
Q_EMIT q->changed();
}
void deleteGroup()
{
const auto selectedGroups = getGroups(selectedRows());
if (selectedGroups.empty()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
for (const auto &group : selectedGroups) {
const bool success = groupsModel->removeGroup(group);
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Removing group from model failed:" << group;
}
}
Q_EMIT q->changed();
}
void certifyGroup()
{
const auto selectedGroups = getGroups(selectedRows());
if (selectedGroups.size() != 1) {
qCDebug(KLEOPATRA_LOG) << __func__ << (selectedGroups.empty() ? "selection is empty" : "more than one group is selected");
return;
}
// execute export group command
auto cmd = new CertifyGroupCommand{selectedGroups.front()};
cmd->setParentWidget(q->window());
cmd->start();
}
void exportGroup()
{
const auto selectedGroups = getGroups(selectedRows());
if (selectedGroups.empty()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
// execute export group command
auto cmd = new ExportGroupsCommand(selectedGroups);
cmd->start();
}
};
GroupsConfigWidget::GroupsConfigWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
GroupsConfigWidget::~GroupsConfigWidget() = default;
void GroupsConfigWidget::setGroups(const std::vector<KeyGroup> &groups)
{
const auto selection = d->saveSelection();
d->groupsModel->setGroups(groups);
d->restoreSelection(selection);
}
std::vector<KeyGroup> GroupsConfigWidget::groups() const
{
std::vector<KeyGroup> result;
result.reserve(d->groupsModel->rowCount());
for (int row = 0; row < d->groupsModel->rowCount(); ++row) {
const QModelIndex index = d->groupsModel->index(row, 0);
result.push_back(d->groupsModel->group(index));
}
return result;
}
#include "groupsconfigwidget.moc"
#include "moc_groupsconfigwidget.cpp"
diff --git a/src/conf/smartcardconfigpage.cpp b/src/conf/smartcardconfigpage.cpp
index 2c81021f3..f0450491e 100644
--- a/src/conf/smartcardconfigpage.cpp
+++ b/src/conf/smartcardconfigpage.cpp
@@ -1,117 +1,116 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/smartcardconfigpage.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 "smartcardconfigpage.h"
#include <Libkleo/Compat>
#include <Libkleo/ReaderPortSelection>
#include <KLocalizedString>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QHBoxLayout>
#include <QLabel>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Config;
using namespace QGpgME;
class SmartCardConfigurationPage::Private
{
public:
Private(SmartCardConfigurationPage *q);
static CryptoConfigEntry *readerPortConfigEntry(const CryptoConfig *config = nullptr);
public:
ReaderPortSelection *const mReaderPort;
};
SmartCardConfigurationPage::Private::Private(SmartCardConfigurationPage *qq)
: mReaderPort{new ReaderPortSelection{qq->widget()}}
{
}
// static
CryptoConfigEntry *SmartCardConfigurationPage::Private::readerPortConfigEntry(const CryptoConfig *config)
{
if (!config) {
config = QGpgME::cryptoConfig();
}
return Kleo::getCryptoConfigEntry(config, "scdaemon", "reader-port");
}
SmartCardConfigurationPage::SmartCardConfigurationPage(QObject *parent, const KPluginMetaData &data)
: KCModule(parent, data)
, d{std::make_unique<Private>(this)}
{
auto mainLayout = new QVBoxLayout{widget()};
- mainLayout->setContentsMargins(0, 0, 0, 0);
{
auto l = new QHBoxLayout{};
l->setContentsMargins(0, 0, 0, 0);
auto label = new QLabel{i18n("Smart card reader to use:"), widget()};
label->setBuddy(d->mReaderPort);
l->addWidget(label);
l->addWidget(d->mReaderPort, 1);
mainLayout->addLayout(l);
connect(d->mReaderPort, &ReaderPortSelection::valueChanged, this, &SmartCardConfigurationPage::markAsChanged);
}
mainLayout->addStretch();
load();
}
SmartCardConfigurationPage::~SmartCardConfigurationPage() = default;
void SmartCardConfigurationPage::load()
{
const auto *const entry = d->readerPortConfigEntry();
if (entry) {
d->mReaderPort->setEnabled(!entry->isReadOnly());
d->mReaderPort->setValue(entry->stringValue());
} else {
d->mReaderPort->setEnabled(false);
d->mReaderPort->setValue(i18n("Cannot be configured with Kleopatra"));
}
}
void SmartCardConfigurationPage::save()
{
auto config = QGpgME::cryptoConfig();
auto const entry = d->readerPortConfigEntry(config);
if (entry && !entry->isReadOnly()) {
entry->setStringValue(d->mReaderPort->value());
}
config->sync(true);
}
void SmartCardConfigurationPage::defaults()
{
const auto *const entry = d->readerPortConfigEntry();
if (entry && !entry->isReadOnly()) {
d->mReaderPort->setValue({});
}
}
#include "moc_smartcardconfigpage.cpp"
diff --git a/src/conf/smimevalidationconfigurationwidget.cpp b/src/conf/smimevalidationconfigurationwidget.cpp
index dd64fc5d7..64400e113 100644
--- a/src/conf/smimevalidationconfigurationwidget.cpp
+++ b/src/conf/smimevalidationconfigurationwidget.cpp
@@ -1,401 +1,397 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/smimevalidationconfigurationwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "smimevalidationconfigurationwidget.h"
#include "ui_smimevalidationconfigurationwidget.h"
#include "labelledwidget.h"
#include "smimevalidationpreferences.h"
#include <Libkleo/Compat>
#include <QGpgME/CryptoConfig>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#if HAVE_QDBUS
#include <QDBusConnection>
#endif
using namespace Kleo;
using namespace Kleo::Config;
using namespace QGpgME;
class SMimeValidationConfigurationWidget::Private
{
friend class ::Kleo::Config::SMimeValidationConfigurationWidget;
SMimeValidationConfigurationWidget *const q;
public:
explicit Private(SMimeValidationConfigurationWidget *qq)
: q(qq)
, ui(qq)
{
#if HAVE_QDBUS
QDBusConnection::sessionBus().connect(QString(), QString(), QStringLiteral("org.kde.kleo.CryptoConfig"), QStringLiteral("changed"), q, SLOT(load()));
#endif
auto changedSignal = &SMimeValidationConfigurationWidget::changed;
connect(ui.intervalRefreshCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.intervalRefreshSB, &QSpinBox::valueChanged, q, changedSignal);
connect(ui.OCSPCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.OCSPResponderURL, &QLineEdit::textChanged, q, changedSignal);
auto certRequesterSignal = &KleopatraClientCopy::Gui::CertificateRequester::selectedCertificatesChanged;
connect(ui.OCSPResponderSignature, certRequesterSignal, q, changedSignal);
connect(ui.doNotCheckCertPolicyCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.neverConsultCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.allowMarkTrustedCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.fetchMissingCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.ignoreServiceURLCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.ignoreHTTPDPCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.disableHTTPCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.honorHTTPProxyRB, &QRadioButton::toggled, q, changedSignal);
connect(ui.useCustomHTTPProxyRB, &QRadioButton::toggled, q, changedSignal);
connect(ui.customHTTPProxy, &QLineEdit::textChanged, q, changedSignal);
connect(ui.ignoreLDAPDPCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.disableLDAPCB, &QCheckBox::toggled, q, changedSignal);
connect(ui.customLDAPProxy, &QLineEdit::textChanged, q, changedSignal);
auto enableDisableSlot = [this]() {
enableDisableActions();
};
connect(ui.useCustomHTTPProxyRB, &QRadioButton::toggled, q, enableDisableSlot);
connect(ui.disableHTTPCB, &QCheckBox::toggled, q, enableDisableSlot);
}
bool customHTTPProxyWritable = false;
private:
void enableDisableActions()
{
ui.customHTTPProxy->setEnabled(ui.useCustomHTTPProxyRB->isChecked() && !ui.disableHTTPCB->isChecked() && customHTTPProxyWritable);
}
private:
struct UI : Ui_SMimeValidationConfigurationWidget {
LabelledWidget<KleopatraClientCopy::Gui::CertificateRequester> labelledOCSPResponderSignature;
LabelledWidget<QLineEdit> labelledOCSPResponderURL;
explicit UI(SMimeValidationConfigurationWidget *q)
: Ui_SMimeValidationConfigurationWidget()
{
setupUi(q);
labelledOCSPResponderURL.setWidgets(OCSPResponderURL, OCSPResponderURLLabel);
labelledOCSPResponderSignature.setWidgets(OCSPResponderSignature, OCSPResponderSignatureLabel);
- if (QLayout *l = q->layout()) {
- l->setContentsMargins(0, 0, 0, 0);
- }
-
OCSPResponderSignature->setOnlyX509CertificatesAllowed(true);
OCSPResponderSignature->setOnlySigningCertificatesAllowed(true);
OCSPResponderSignature->setMultipleCertificatesAllowed(false);
// OCSPResponderSignature->setAllowedKeys( KeySelectionDialog::TrustedKeys|KeySelectionDialog::ValidKeys );
}
} ui;
};
SMimeValidationConfigurationWidget::SMimeValidationConfigurationWidget(QWidget *p, Qt::WindowFlags f)
: QWidget(p, f)
, d(new Private(this))
{
}
SMimeValidationConfigurationWidget::~SMimeValidationConfigurationWidget()
{
}
static void disableDirmngrWidget(QWidget *w)
{
w->setEnabled(false);
w->setWhatsThis(i18n("This option requires dirmngr >= 0.9.0"));
}
static void initializeDirmngrCheckbox(QCheckBox *cb, CryptoConfigEntry *entry)
{
if (entry) {
cb->setChecked(entry->boolValue());
}
if (!entry || entry->isReadOnly()) {
disableDirmngrWidget(cb);
}
}
struct SMIMECryptoConfigEntries {
enum ShowError {
DoNotShowError,
DoShowError,
};
SMIMECryptoConfigEntries(CryptoConfig *config)
: mConfig(config)
// Checkboxes
, mCheckUsingOCSPConfigEntry(configEntry("gpgsm", "enable-ocsp", CryptoConfigEntry::ArgType_None))
, mEnableOCSPsendingConfigEntry(configEntry("dirmngr", "allow-ocsp", CryptoConfigEntry::ArgType_None))
, mDoNotCheckCertPolicyConfigEntry(configEntry("gpgsm", "disable-policy-checks", CryptoConfigEntry::ArgType_None))
, mNeverConsultConfigEntry(configEntry("gpgsm", "disable-crl-checks", CryptoConfigEntry::ArgType_None))
, mAllowMarkTrustedConfigEntry(
configEntry("gpg-agent", "allow-mark-trusted", CryptoConfigEntry::ArgType_None, DoNotShowError)) // legacy entry -> ignore error
, mFetchMissingConfigEntry(configEntry("gpgsm", "auto-issuer-key-retrieve", CryptoConfigEntry::ArgType_None))
, mNoAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "no-allow-mark-trusted", CryptoConfigEntry::ArgType_None))
// dirmngr-0.9.0 options
, mIgnoreServiceURLEntry(configEntry("dirmngr", "ignore-ocsp-service-url", CryptoConfigEntry::ArgType_None))
, mIgnoreHTTPDPEntry(configEntry("dirmngr", "ignore-http-dp", CryptoConfigEntry::ArgType_None))
, mDisableHTTPEntry(configEntry("dirmngr", "disable-http", CryptoConfigEntry::ArgType_None))
, mHonorHTTPProxy(configEntry("dirmngr", "honor-http-proxy", CryptoConfigEntry::ArgType_None))
, mIgnoreLDAPDPEntry(configEntry("dirmngr", "ignore-ldap-dp", CryptoConfigEntry::ArgType_None))
, mDisableLDAPEntry(configEntry("dirmngr", "disable-ldap", CryptoConfigEntry::ArgType_None))
// Other widgets
, mOCSPResponderURLConfigEntry(configEntry("dirmngr", "ocsp-responder", CryptoConfigEntry::ArgType_String))
, mOCSPResponderSignature(configEntry("dirmngr", "ocsp-signer", CryptoConfigEntry::ArgType_String))
, mCustomHTTPProxy(configEntry("dirmngr", "http-proxy", CryptoConfigEntry::ArgType_String))
, mCustomLDAPProxy(configEntry("dirmngr", "ldap-proxy", CryptoConfigEntry::ArgType_String))
{
}
CryptoConfigEntry *configEntry(const char *componentName, const char *entryName, int argType, ShowError showError = DoShowError);
CryptoConfig *const mConfig;
// Checkboxes
CryptoConfigEntry *const mCheckUsingOCSPConfigEntry;
CryptoConfigEntry *const mEnableOCSPsendingConfigEntry;
CryptoConfigEntry *const mDoNotCheckCertPolicyConfigEntry;
CryptoConfigEntry *const mNeverConsultConfigEntry;
CryptoConfigEntry *const mAllowMarkTrustedConfigEntry;
CryptoConfigEntry *const mFetchMissingConfigEntry;
// gnupg 2.0.17+ option that should inhibit allow-mark-trusted display
CryptoConfigEntry *const mNoAllowMarkTrustedConfigEntry;
// dirmngr-0.9.0 options
CryptoConfigEntry *const mIgnoreServiceURLEntry;
CryptoConfigEntry *const mIgnoreHTTPDPEntry;
CryptoConfigEntry *const mDisableHTTPEntry;
CryptoConfigEntry *const mHonorHTTPProxy;
CryptoConfigEntry *const mIgnoreLDAPDPEntry;
CryptoConfigEntry *const mDisableLDAPEntry;
// Other widgets
CryptoConfigEntry *const mOCSPResponderURLConfigEntry;
CryptoConfigEntry *const mOCSPResponderSignature;
CryptoConfigEntry *const mCustomHTTPProxy;
CryptoConfigEntry *const mCustomLDAPProxy;
};
void SMimeValidationConfigurationWidget::defaults()
{
qCDebug(KLEOPATRA_LOG) << "not implemented";
}
void SMimeValidationConfigurationWidget::load()
{
const SMimeValidationPreferences preferences;
const unsigned int refreshInterval = preferences.refreshInterval();
d->ui.intervalRefreshCB->setChecked(refreshInterval > 0);
d->ui.intervalRefreshSB->setValue(refreshInterval);
const bool isRefreshIntervalImmutable = preferences.isImmutable(QStringLiteral("RefreshInterval"));
d->ui.intervalRefreshCB->setEnabled(!isRefreshIntervalImmutable);
d->ui.intervalRefreshSB->setEnabled(!isRefreshIntervalImmutable);
CryptoConfig *const config = QGpgME::cryptoConfig();
if (!config) {
setEnabled(false);
return;
}
#if 0
// crashes other pages' save() by nuking the CryptoConfigEntries under their feet.
// This was probably not a problem in KMail, where this code comes
// from. But here, it's fatal.
// Force re-parsing gpgconf data, in case e.g. kleopatra or "configure backend" was used
// (which ends up calling us via D-Bus)
config->clear();
#endif
// Create config entries
// Don't keep them around, they'll get deleted by clear(), which could be
// done by the "configure backend" button even before we save().
const SMIMECryptoConfigEntries e(config);
// Initialize GUI items from the config entries
if (e.mCheckUsingOCSPConfigEntry) {
d->ui.OCSPCB->setChecked(e.mCheckUsingOCSPConfigEntry->boolValue());
}
d->ui.OCSPCB->setEnabled(e.mCheckUsingOCSPConfigEntry && !e.mCheckUsingOCSPConfigEntry->isReadOnly());
d->ui.OCSPGroupBox->setEnabled(d->ui.OCSPCB->isChecked());
if (e.mDoNotCheckCertPolicyConfigEntry) {
d->ui.doNotCheckCertPolicyCB->setChecked(e.mDoNotCheckCertPolicyConfigEntry->boolValue());
}
d->ui.doNotCheckCertPolicyCB->setEnabled(e.mDoNotCheckCertPolicyConfigEntry && !e.mDoNotCheckCertPolicyConfigEntry->isReadOnly());
if (e.mNeverConsultConfigEntry) {
d->ui.neverConsultCB->setChecked(e.mNeverConsultConfigEntry->boolValue());
}
d->ui.neverConsultCB->setEnabled(e.mNeverConsultConfigEntry && !e.mNeverConsultConfigEntry->isReadOnly());
if (e.mNoAllowMarkTrustedConfigEntry) {
d->ui.allowMarkTrustedCB->hide(); // this option was only here to _enable_ allow-mark-trusted, and makes no sense if it's already default on
}
if (e.mAllowMarkTrustedConfigEntry) {
d->ui.allowMarkTrustedCB->setChecked(e.mAllowMarkTrustedConfigEntry->boolValue());
}
d->ui.allowMarkTrustedCB->setEnabled(e.mAllowMarkTrustedConfigEntry && !e.mAllowMarkTrustedConfigEntry->isReadOnly());
if (e.mFetchMissingConfigEntry) {
d->ui.fetchMissingCB->setChecked(e.mFetchMissingConfigEntry->boolValue());
}
d->ui.fetchMissingCB->setEnabled(e.mFetchMissingConfigEntry && !e.mFetchMissingConfigEntry->isReadOnly());
if (e.mOCSPResponderURLConfigEntry) {
d->ui.OCSPResponderURL->setText(e.mOCSPResponderURLConfigEntry->stringValue());
}
d->ui.labelledOCSPResponderURL.setEnabled(e.mOCSPResponderURLConfigEntry && !e.mOCSPResponderURLConfigEntry->isReadOnly());
if (e.mOCSPResponderSignature) {
d->ui.OCSPResponderSignature->setSelectedCertificate(e.mOCSPResponderSignature->stringValue());
}
d->ui.labelledOCSPResponderSignature.setEnabled(e.mOCSPResponderSignature && !e.mOCSPResponderSignature->isReadOnly());
// dirmngr-0.9.0 options
initializeDirmngrCheckbox(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
initializeDirmngrCheckbox(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
initializeDirmngrCheckbox(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
initializeDirmngrCheckbox(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
initializeDirmngrCheckbox(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
if (e.mCustomHTTPProxy) {
QString systemProxy = QString::fromLocal8Bit(qgetenv("http_proxy"));
if (systemProxy.isEmpty()) {
systemProxy = i18n("no proxy");
}
d->ui.systemHTTPProxy->setText(i18n("(Current system setting: %1)", systemProxy));
const bool honor = e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue();
d->ui.honorHTTPProxyRB->setChecked(honor);
d->ui.useCustomHTTPProxyRB->setChecked(!honor);
d->ui.customHTTPProxy->setText(e.mCustomHTTPProxy->stringValue());
}
d->customHTTPProxyWritable = e.mCustomHTTPProxy && !e.mCustomHTTPProxy->isReadOnly();
if (!d->customHTTPProxyWritable) {
disableDirmngrWidget(d->ui.honorHTTPProxyRB);
disableDirmngrWidget(d->ui.useCustomHTTPProxyRB);
disableDirmngrWidget(d->ui.systemHTTPProxy);
disableDirmngrWidget(d->ui.customHTTPProxy);
}
if (e.mCustomLDAPProxy) {
d->ui.customLDAPProxy->setText(e.mCustomLDAPProxy->stringValue());
}
if (!e.mCustomLDAPProxy || e.mCustomLDAPProxy->isReadOnly()) {
disableDirmngrWidget(d->ui.customLDAPProxy);
disableDirmngrWidget(d->ui.customLDAPLabel);
}
d->enableDisableActions();
}
static void saveCheckBoxToKleoEntry(QCheckBox *cb, CryptoConfigEntry *entry)
{
const bool b = cb->isChecked();
if (entry && entry->boolValue() != b) {
entry->setBoolValue(b);
}
}
void SMimeValidationConfigurationWidget::save() const
{
CryptoConfig *const config = QGpgME::cryptoConfig();
if (!config) {
return;
}
{
SMimeValidationPreferences preferences;
preferences.setRefreshInterval(d->ui.intervalRefreshCB->isChecked() ? d->ui.intervalRefreshSB->value() : 0);
preferences.save();
}
// Create config entries
// Don't keep them around, they'll get deleted by clear(), which could be done by the
// "configure backend" button.
const SMIMECryptoConfigEntries e(config);
const bool b = d->ui.OCSPCB->isChecked();
if (e.mCheckUsingOCSPConfigEntry && e.mCheckUsingOCSPConfigEntry->boolValue() != b) {
e.mCheckUsingOCSPConfigEntry->setBoolValue(b);
}
// Set allow-ocsp together with enable-ocsp
if (e.mEnableOCSPsendingConfigEntry && e.mEnableOCSPsendingConfigEntry->boolValue() != b) {
e.mEnableOCSPsendingConfigEntry->setBoolValue(b);
}
saveCheckBoxToKleoEntry(d->ui.doNotCheckCertPolicyCB, e.mDoNotCheckCertPolicyConfigEntry);
saveCheckBoxToKleoEntry(d->ui.neverConsultCB, e.mNeverConsultConfigEntry);
saveCheckBoxToKleoEntry(d->ui.allowMarkTrustedCB, e.mAllowMarkTrustedConfigEntry);
saveCheckBoxToKleoEntry(d->ui.fetchMissingCB, e.mFetchMissingConfigEntry);
QString txt = d->ui.OCSPResponderURL->text();
if (e.mOCSPResponderURLConfigEntry && e.mOCSPResponderURLConfigEntry->stringValue() != txt) {
e.mOCSPResponderURLConfigEntry->setStringValue(txt);
}
txt = d->ui.OCSPResponderSignature->selectedCertificate();
if (e.mOCSPResponderSignature && e.mOCSPResponderSignature->stringValue() != txt) {
e.mOCSPResponderSignature->setStringValue(txt);
}
// dirmngr-0.9.0 options
saveCheckBoxToKleoEntry(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
saveCheckBoxToKleoEntry(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
saveCheckBoxToKleoEntry(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
saveCheckBoxToKleoEntry(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
saveCheckBoxToKleoEntry(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
if (e.mCustomHTTPProxy) {
const bool honor = d->ui.honorHTTPProxyRB->isChecked();
if (e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue() != honor) {
e.mHonorHTTPProxy->setBoolValue(honor);
}
const QString chosenProxy = d->ui.customHTTPProxy->text();
if (chosenProxy != e.mCustomHTTPProxy->stringValue()) {
e.mCustomHTTPProxy->setStringValue(chosenProxy);
}
}
txt = d->ui.customLDAPProxy->text();
if (e.mCustomLDAPProxy && e.mCustomLDAPProxy->stringValue() != txt) {
e.mCustomLDAPProxy->setStringValue(d->ui.customLDAPProxy->text());
}
config->sync(true);
}
CryptoConfigEntry *
SMIMECryptoConfigEntries::configEntry(const char *componentName, const char *entryName, int /*CryptoConfigEntry::ArgType*/ argType, ShowError showError)
{
CryptoConfigEntry *const entry = getCryptoConfigEntry(mConfig, componentName, entryName);
if (!entry) {
if (showError == DoShowError) {
qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf doesn't seem to know the entry for %1/%2")
.arg(QLatin1String(componentName), QLatin1String(entryName));
}
return nullptr;
}
if (entry->argType() != argType || entry->isList()) {
if (showError == DoShowError) {
qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf has wrong type for %1/%2: %3 %4")
.arg(QLatin1String(componentName), QLatin1String(entryName))
.arg(entry->argType())
.arg(entry->isList());
}
return nullptr;
}
return entry;
}
#include "moc_smimevalidationconfigurationwidget.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:21 PM (21 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e9/1d/599038989a5762ad28c0563e6d63

Event Timeline