File indexing completed on 2024-06-02 05:24:20

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     conf/smimevalidationconfigurationwidget.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "smimevalidationconfigurationwidget.h"
0013 
0014 #include "ui_smimevalidationconfigurationwidget.h"
0015 
0016 #include "labelledwidget.h"
0017 
0018 #include "smimevalidationpreferences.h"
0019 
0020 #include <Libkleo/Compat>
0021 
0022 #include <QGpgME/CryptoConfig>
0023 
0024 #include "kleopatra_debug.h"
0025 #include <KLocalizedString>
0026 
0027 #if HAVE_QDBUS
0028 #include <QDBusConnection>
0029 #endif
0030 
0031 using namespace Kleo;
0032 using namespace Kleo::Config;
0033 using namespace QGpgME;
0034 
0035 class SMimeValidationConfigurationWidget::Private
0036 {
0037     friend class ::Kleo::Config::SMimeValidationConfigurationWidget;
0038     SMimeValidationConfigurationWidget *const q;
0039 
0040 public:
0041     explicit Private(SMimeValidationConfigurationWidget *qq)
0042         : q(qq)
0043         , ui(qq)
0044     {
0045 #if HAVE_QDBUS
0046         QDBusConnection::sessionBus().connect(QString(), QString(), QStringLiteral("org.kde.kleo.CryptoConfig"), QStringLiteral("changed"), q, SLOT(load()));
0047 #endif
0048         auto changedSignal = &SMimeValidationConfigurationWidget::changed;
0049         connect(ui.intervalRefreshCB, &QCheckBox::toggled, q, changedSignal);
0050         connect(ui.intervalRefreshSB, &QSpinBox::valueChanged, q, changedSignal);
0051         connect(ui.OCSPCB, &QCheckBox::toggled, q, changedSignal);
0052         connect(ui.OCSPResponderURL, &QLineEdit::textChanged, q, changedSignal);
0053 
0054         auto certRequesterSignal = &KleopatraClientCopy::Gui::CertificateRequester::selectedCertificatesChanged;
0055         connect(ui.OCSPResponderSignature, certRequesterSignal, q, changedSignal);
0056 
0057         connect(ui.doNotCheckCertPolicyCB, &QCheckBox::toggled, q, changedSignal);
0058         connect(ui.neverConsultCB, &QCheckBox::toggled, q, changedSignal);
0059         connect(ui.allowMarkTrustedCB, &QCheckBox::toggled, q, changedSignal);
0060         connect(ui.fetchMissingCB, &QCheckBox::toggled, q, changedSignal);
0061         connect(ui.ignoreServiceURLCB, &QCheckBox::toggled, q, changedSignal);
0062         connect(ui.ignoreHTTPDPCB, &QCheckBox::toggled, q, changedSignal);
0063         connect(ui.disableHTTPCB, &QCheckBox::toggled, q, changedSignal);
0064         connect(ui.honorHTTPProxyRB, &QRadioButton::toggled, q, changedSignal);
0065         connect(ui.useCustomHTTPProxyRB, &QRadioButton::toggled, q, changedSignal);
0066         connect(ui.customHTTPProxy, &QLineEdit::textChanged, q, changedSignal);
0067         connect(ui.ignoreLDAPDPCB, &QCheckBox::toggled, q, changedSignal);
0068         connect(ui.disableLDAPCB, &QCheckBox::toggled, q, changedSignal);
0069         connect(ui.customLDAPProxy, &QLineEdit::textChanged, q, changedSignal);
0070 
0071         auto enableDisableSlot = [this]() {
0072             enableDisableActions();
0073         };
0074         connect(ui.useCustomHTTPProxyRB, &QRadioButton::toggled, q, enableDisableSlot);
0075         connect(ui.disableHTTPCB, &QCheckBox::toggled, q, enableDisableSlot);
0076     }
0077 
0078     bool customHTTPProxyWritable = false;
0079 
0080 private:
0081     void enableDisableActions()
0082     {
0083         ui.customHTTPProxy->setEnabled(ui.useCustomHTTPProxyRB->isChecked() && !ui.disableHTTPCB->isChecked() && customHTTPProxyWritable);
0084     }
0085 
0086 private:
0087     struct UI : Ui_SMimeValidationConfigurationWidget {
0088         LabelledWidget<KleopatraClientCopy::Gui::CertificateRequester> labelledOCSPResponderSignature;
0089         LabelledWidget<QLineEdit> labelledOCSPResponderURL;
0090 
0091         explicit UI(SMimeValidationConfigurationWidget *q)
0092             : Ui_SMimeValidationConfigurationWidget()
0093         {
0094             setupUi(q);
0095 
0096             labelledOCSPResponderURL.setWidgets(OCSPResponderURL, OCSPResponderURLLabel);
0097             labelledOCSPResponderSignature.setWidgets(OCSPResponderSignature, OCSPResponderSignatureLabel);
0098 
0099             OCSPResponderSignature->setOnlyX509CertificatesAllowed(true);
0100             OCSPResponderSignature->setOnlySigningCertificatesAllowed(true);
0101             OCSPResponderSignature->setMultipleCertificatesAllowed(false);
0102             // OCSPResponderSignature->setAllowedKeys( KeySelectionDialog::TrustedKeys|KeySelectionDialog::ValidKeys );
0103         }
0104     } ui;
0105 };
0106 
0107 SMimeValidationConfigurationWidget::SMimeValidationConfigurationWidget(QWidget *p, Qt::WindowFlags f)
0108     : QWidget(p, f)
0109     , d(new Private(this))
0110 {
0111 }
0112 
0113 SMimeValidationConfigurationWidget::~SMimeValidationConfigurationWidget()
0114 {
0115 }
0116 
0117 static void disableDirmngrWidget(QWidget *w)
0118 {
0119     w->setEnabled(false);
0120     w->setWhatsThis(i18n("This option requires dirmngr >= 0.9.0"));
0121 }
0122 
0123 static void initializeDirmngrCheckbox(QCheckBox *cb, CryptoConfigEntry *entry)
0124 {
0125     if (entry) {
0126         cb->setChecked(entry->boolValue());
0127     }
0128     if (!entry || entry->isReadOnly()) {
0129         disableDirmngrWidget(cb);
0130     }
0131 }
0132 
0133 struct SMIMECryptoConfigEntries {
0134     enum ShowError {
0135         DoNotShowError,
0136         DoShowError,
0137     };
0138 
0139     SMIMECryptoConfigEntries(CryptoConfig *config)
0140         : mConfig(config)
0141         // Checkboxes
0142         , mCheckUsingOCSPConfigEntry(configEntry("gpgsm", "enable-ocsp", CryptoConfigEntry::ArgType_None))
0143         , mEnableOCSPsendingConfigEntry(configEntry("dirmngr", "allow-ocsp", CryptoConfigEntry::ArgType_None))
0144         , mDoNotCheckCertPolicyConfigEntry(configEntry("gpgsm", "disable-policy-checks", CryptoConfigEntry::ArgType_None))
0145         , mNeverConsultConfigEntry(configEntry("gpgsm", "disable-crl-checks", CryptoConfigEntry::ArgType_None))
0146         , mAllowMarkTrustedConfigEntry(
0147               configEntry("gpg-agent", "allow-mark-trusted", CryptoConfigEntry::ArgType_None, DoNotShowError)) // legacy entry -> ignore error
0148         , mFetchMissingConfigEntry(configEntry("gpgsm", "auto-issuer-key-retrieve", CryptoConfigEntry::ArgType_None))
0149         , mNoAllowMarkTrustedConfigEntry(configEntry("gpg-agent", "no-allow-mark-trusted", CryptoConfigEntry::ArgType_None))
0150         // dirmngr-0.9.0 options
0151         , mIgnoreServiceURLEntry(configEntry("dirmngr", "ignore-ocsp-service-url", CryptoConfigEntry::ArgType_None))
0152         , mIgnoreHTTPDPEntry(configEntry("dirmngr", "ignore-http-dp", CryptoConfigEntry::ArgType_None))
0153         , mDisableHTTPEntry(configEntry("dirmngr", "disable-http", CryptoConfigEntry::ArgType_None))
0154         , mHonorHTTPProxy(configEntry("dirmngr", "honor-http-proxy", CryptoConfigEntry::ArgType_None))
0155         , mIgnoreLDAPDPEntry(configEntry("dirmngr", "ignore-ldap-dp", CryptoConfigEntry::ArgType_None))
0156         , mDisableLDAPEntry(configEntry("dirmngr", "disable-ldap", CryptoConfigEntry::ArgType_None))
0157         // Other widgets
0158         , mOCSPResponderURLConfigEntry(configEntry("dirmngr", "ocsp-responder", CryptoConfigEntry::ArgType_String))
0159         , mOCSPResponderSignature(configEntry("dirmngr", "ocsp-signer", CryptoConfigEntry::ArgType_String))
0160         , mCustomHTTPProxy(configEntry("dirmngr", "http-proxy", CryptoConfigEntry::ArgType_String))
0161         , mCustomLDAPProxy(configEntry("dirmngr", "ldap-proxy", CryptoConfigEntry::ArgType_String))
0162     {
0163     }
0164 
0165     CryptoConfigEntry *configEntry(const char *componentName, const char *entryName, int argType, ShowError showError = DoShowError);
0166 
0167     CryptoConfig *const mConfig;
0168 
0169     // Checkboxes
0170     CryptoConfigEntry *const mCheckUsingOCSPConfigEntry;
0171     CryptoConfigEntry *const mEnableOCSPsendingConfigEntry;
0172     CryptoConfigEntry *const mDoNotCheckCertPolicyConfigEntry;
0173     CryptoConfigEntry *const mNeverConsultConfigEntry;
0174     CryptoConfigEntry *const mAllowMarkTrustedConfigEntry;
0175     CryptoConfigEntry *const mFetchMissingConfigEntry;
0176     // gnupg 2.0.17+ option that should inhibit allow-mark-trusted display
0177     CryptoConfigEntry *const mNoAllowMarkTrustedConfigEntry;
0178     // dirmngr-0.9.0 options
0179     CryptoConfigEntry *const mIgnoreServiceURLEntry;
0180     CryptoConfigEntry *const mIgnoreHTTPDPEntry;
0181     CryptoConfigEntry *const mDisableHTTPEntry;
0182     CryptoConfigEntry *const mHonorHTTPProxy;
0183     CryptoConfigEntry *const mIgnoreLDAPDPEntry;
0184     CryptoConfigEntry *const mDisableLDAPEntry;
0185     // Other widgets
0186     CryptoConfigEntry *const mOCSPResponderURLConfigEntry;
0187     CryptoConfigEntry *const mOCSPResponderSignature;
0188     CryptoConfigEntry *const mCustomHTTPProxy;
0189     CryptoConfigEntry *const mCustomLDAPProxy;
0190 };
0191 
0192 void SMimeValidationConfigurationWidget::defaults()
0193 {
0194     qCDebug(KLEOPATRA_LOG) << "not implemented";
0195 }
0196 
0197 void SMimeValidationConfigurationWidget::load()
0198 {
0199     const SMimeValidationPreferences preferences;
0200     const unsigned int refreshInterval = preferences.refreshInterval();
0201     d->ui.intervalRefreshCB->setChecked(refreshInterval > 0);
0202     d->ui.intervalRefreshSB->setValue(refreshInterval);
0203     const bool isRefreshIntervalImmutable = preferences.isImmutable(QStringLiteral("RefreshInterval"));
0204     d->ui.intervalRefreshCB->setEnabled(!isRefreshIntervalImmutable);
0205     d->ui.intervalRefreshSB->setEnabled(!isRefreshIntervalImmutable);
0206 
0207     CryptoConfig *const config = QGpgME::cryptoConfig();
0208     if (!config) {
0209         setEnabled(false);
0210         return;
0211     }
0212 
0213 #if 0
0214     // crashes other pages' save() by nuking the CryptoConfigEntries under their feet.
0215     // This was probably not a problem in KMail, where this code comes
0216     // from. But here, it's fatal.
0217 
0218     // Force re-parsing gpgconf data, in case e.g. kleopatra or "configure backend" was used
0219     // (which ends up calling us via D-Bus)
0220     config->clear();
0221 #endif
0222 
0223     // Create config entries
0224     // Don't keep them around, they'll get deleted by clear(), which could be
0225     // done by the "configure backend" button even before we save().
0226     const SMIMECryptoConfigEntries e(config);
0227 
0228     // Initialize GUI items from the config entries
0229 
0230     if (e.mCheckUsingOCSPConfigEntry) {
0231         d->ui.OCSPCB->setChecked(e.mCheckUsingOCSPConfigEntry->boolValue());
0232     }
0233     d->ui.OCSPCB->setEnabled(e.mCheckUsingOCSPConfigEntry && !e.mCheckUsingOCSPConfigEntry->isReadOnly());
0234     d->ui.OCSPGroupBox->setEnabled(d->ui.OCSPCB->isChecked());
0235 
0236     if (e.mDoNotCheckCertPolicyConfigEntry) {
0237         d->ui.doNotCheckCertPolicyCB->setChecked(e.mDoNotCheckCertPolicyConfigEntry->boolValue());
0238     }
0239     d->ui.doNotCheckCertPolicyCB->setEnabled(e.mDoNotCheckCertPolicyConfigEntry && !e.mDoNotCheckCertPolicyConfigEntry->isReadOnly());
0240     if (e.mNeverConsultConfigEntry) {
0241         d->ui.neverConsultCB->setChecked(e.mNeverConsultConfigEntry->boolValue());
0242     }
0243     d->ui.neverConsultCB->setEnabled(e.mNeverConsultConfigEntry && !e.mNeverConsultConfigEntry->isReadOnly());
0244     if (e.mNoAllowMarkTrustedConfigEntry) {
0245         d->ui.allowMarkTrustedCB->hide(); // this option was only here to _enable_ allow-mark-trusted, and makes no sense if it's already default on
0246     }
0247     if (e.mAllowMarkTrustedConfigEntry) {
0248         d->ui.allowMarkTrustedCB->setChecked(e.mAllowMarkTrustedConfigEntry->boolValue());
0249     }
0250     d->ui.allowMarkTrustedCB->setEnabled(e.mAllowMarkTrustedConfigEntry && !e.mAllowMarkTrustedConfigEntry->isReadOnly());
0251     if (e.mFetchMissingConfigEntry) {
0252         d->ui.fetchMissingCB->setChecked(e.mFetchMissingConfigEntry->boolValue());
0253     }
0254     d->ui.fetchMissingCB->setEnabled(e.mFetchMissingConfigEntry && !e.mFetchMissingConfigEntry->isReadOnly());
0255 
0256     if (e.mOCSPResponderURLConfigEntry) {
0257         d->ui.OCSPResponderURL->setText(e.mOCSPResponderURLConfigEntry->stringValue());
0258     }
0259     d->ui.labelledOCSPResponderURL.setEnabled(e.mOCSPResponderURLConfigEntry && !e.mOCSPResponderURLConfigEntry->isReadOnly());
0260     if (e.mOCSPResponderSignature) {
0261         d->ui.OCSPResponderSignature->setSelectedCertificate(e.mOCSPResponderSignature->stringValue());
0262     }
0263     d->ui.labelledOCSPResponderSignature.setEnabled(e.mOCSPResponderSignature && !e.mOCSPResponderSignature->isReadOnly());
0264 
0265     // dirmngr-0.9.0 options
0266     initializeDirmngrCheckbox(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
0267     initializeDirmngrCheckbox(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
0268     initializeDirmngrCheckbox(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
0269     initializeDirmngrCheckbox(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
0270     initializeDirmngrCheckbox(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
0271     if (e.mCustomHTTPProxy) {
0272         QString systemProxy = QString::fromLocal8Bit(qgetenv("http_proxy"));
0273         if (systemProxy.isEmpty()) {
0274             systemProxy = i18n("no proxy");
0275         }
0276         d->ui.systemHTTPProxy->setText(i18n("(Current system setting: %1)", systemProxy));
0277         const bool honor = e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue();
0278         d->ui.honorHTTPProxyRB->setChecked(honor);
0279         d->ui.useCustomHTTPProxyRB->setChecked(!honor);
0280         d->ui.customHTTPProxy->setText(e.mCustomHTTPProxy->stringValue());
0281     }
0282     d->customHTTPProxyWritable = e.mCustomHTTPProxy && !e.mCustomHTTPProxy->isReadOnly();
0283     if (!d->customHTTPProxyWritable) {
0284         disableDirmngrWidget(d->ui.honorHTTPProxyRB);
0285         disableDirmngrWidget(d->ui.useCustomHTTPProxyRB);
0286         disableDirmngrWidget(d->ui.systemHTTPProxy);
0287         disableDirmngrWidget(d->ui.customHTTPProxy);
0288     }
0289     if (e.mCustomLDAPProxy) {
0290         d->ui.customLDAPProxy->setText(e.mCustomLDAPProxy->stringValue());
0291     }
0292     if (!e.mCustomLDAPProxy || e.mCustomLDAPProxy->isReadOnly()) {
0293         disableDirmngrWidget(d->ui.customLDAPProxy);
0294         disableDirmngrWidget(d->ui.customLDAPLabel);
0295     }
0296     d->enableDisableActions();
0297 }
0298 
0299 static void saveCheckBoxToKleoEntry(QCheckBox *cb, CryptoConfigEntry *entry)
0300 {
0301     const bool b = cb->isChecked();
0302     if (entry && entry->boolValue() != b) {
0303         entry->setBoolValue(b);
0304     }
0305 }
0306 
0307 void SMimeValidationConfigurationWidget::save() const
0308 {
0309     CryptoConfig *const config = QGpgME::cryptoConfig();
0310     if (!config) {
0311         return;
0312     }
0313 
0314     {
0315         SMimeValidationPreferences preferences;
0316         preferences.setRefreshInterval(d->ui.intervalRefreshCB->isChecked() ? d->ui.intervalRefreshSB->value() : 0);
0317         preferences.save();
0318     }
0319 
0320     // Create config entries
0321     // Don't keep them around, they'll get deleted by clear(), which could be done by the
0322     // "configure backend" button.
0323     const SMIMECryptoConfigEntries e(config);
0324 
0325     const bool b = d->ui.OCSPCB->isChecked();
0326     if (e.mCheckUsingOCSPConfigEntry && e.mCheckUsingOCSPConfigEntry->boolValue() != b) {
0327         e.mCheckUsingOCSPConfigEntry->setBoolValue(b);
0328     }
0329     // Set allow-ocsp together with enable-ocsp
0330     if (e.mEnableOCSPsendingConfigEntry && e.mEnableOCSPsendingConfigEntry->boolValue() != b) {
0331         e.mEnableOCSPsendingConfigEntry->setBoolValue(b);
0332     }
0333 
0334     saveCheckBoxToKleoEntry(d->ui.doNotCheckCertPolicyCB, e.mDoNotCheckCertPolicyConfigEntry);
0335     saveCheckBoxToKleoEntry(d->ui.neverConsultCB, e.mNeverConsultConfigEntry);
0336     saveCheckBoxToKleoEntry(d->ui.allowMarkTrustedCB, e.mAllowMarkTrustedConfigEntry);
0337     saveCheckBoxToKleoEntry(d->ui.fetchMissingCB, e.mFetchMissingConfigEntry);
0338 
0339     QString txt = d->ui.OCSPResponderURL->text();
0340     if (e.mOCSPResponderURLConfigEntry && e.mOCSPResponderURLConfigEntry->stringValue() != txt) {
0341         e.mOCSPResponderURLConfigEntry->setStringValue(txt);
0342     }
0343 
0344     txt = d->ui.OCSPResponderSignature->selectedCertificate();
0345     if (e.mOCSPResponderSignature && e.mOCSPResponderSignature->stringValue() != txt) {
0346         e.mOCSPResponderSignature->setStringValue(txt);
0347     }
0348 
0349     // dirmngr-0.9.0 options
0350     saveCheckBoxToKleoEntry(d->ui.ignoreServiceURLCB, e.mIgnoreServiceURLEntry);
0351     saveCheckBoxToKleoEntry(d->ui.ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry);
0352     saveCheckBoxToKleoEntry(d->ui.disableHTTPCB, e.mDisableHTTPEntry);
0353     saveCheckBoxToKleoEntry(d->ui.ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry);
0354     saveCheckBoxToKleoEntry(d->ui.disableLDAPCB, e.mDisableLDAPEntry);
0355     if (e.mCustomHTTPProxy) {
0356         const bool honor = d->ui.honorHTTPProxyRB->isChecked();
0357         if (e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue() != honor) {
0358             e.mHonorHTTPProxy->setBoolValue(honor);
0359         }
0360 
0361         const QString chosenProxy = d->ui.customHTTPProxy->text();
0362         if (chosenProxy != e.mCustomHTTPProxy->stringValue()) {
0363             e.mCustomHTTPProxy->setStringValue(chosenProxy);
0364         }
0365     }
0366     txt = d->ui.customLDAPProxy->text();
0367     if (e.mCustomLDAPProxy && e.mCustomLDAPProxy->stringValue() != txt) {
0368         e.mCustomLDAPProxy->setStringValue(d->ui.customLDAPProxy->text());
0369     }
0370 
0371     config->sync(true);
0372 }
0373 
0374 CryptoConfigEntry *
0375 SMIMECryptoConfigEntries::configEntry(const char *componentName, const char *entryName, int /*CryptoConfigEntry::ArgType*/ argType, ShowError showError)
0376 {
0377     CryptoConfigEntry *const entry = getCryptoConfigEntry(mConfig, componentName, entryName);
0378     if (!entry) {
0379         if (showError == DoShowError) {
0380             qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf doesn't seem to know the entry for %1/%2")
0381                                             .arg(QLatin1StringView(componentName), QLatin1String(entryName));
0382         }
0383         return nullptr;
0384     }
0385     if (entry->argType() != argType || entry->isList()) {
0386         if (showError == DoShowError) {
0387             qCWarning(KLEOPATRA_LOG) << QStringLiteral("Backend error: gpgconf has wrong type for %1/%2: %3 %4")
0388                                             .arg(QLatin1StringView(componentName), QLatin1String(entryName))
0389                                             .arg(entry->argType())
0390                                             .arg(entry->isList());
0391         }
0392         return nullptr;
0393     }
0394     return entry;
0395 }
0396 
0397 #include "moc_smimevalidationconfigurationwidget.cpp"