File indexing completed on 2025-01-05 04:55:49

0001 /*  -*- c++ -*-
0002     keyrequester.cpp
0003 
0004     This file is part of libkleopatra, the KDE keymanagement library
0005     SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
0006 
0007     Based on kpgpui.cpp
0008     SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
0009     See file libkdenetwork/AUTHORS.kpgp for details
0010 
0011     This file is part of KPGP, the KDE PGP/GnuPG support library.
0012 
0013     SPDX-License-Identifier: GPL-2.0-or-later
0014  */
0015 
0016 #include <config-libkleo.h>
0017 
0018 #include "keyrequester.h"
0019 
0020 #include "keyselectiondialog.h"
0021 
0022 #include <libkleo/algorithm.h>
0023 #include <libkleo/compliance.h>
0024 #include <libkleo/dn.h>
0025 #include <libkleo/formatting.h>
0026 #include <libkleo/keyhelpers.h>
0027 
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 
0031 #include <QGpgME/KeyListJob>
0032 
0033 #include <QApplication>
0034 #include <QDialog>
0035 #include <QHBoxLayout>
0036 #include <QPushButton>
0037 #include <QString>
0038 
0039 #include <gpgme++/key.h>
0040 #include <gpgme++/keylistresult.h>
0041 
0042 using namespace QGpgME;
0043 using namespace Kleo;
0044 
0045 Kleo::KeyRequester::KeyRequester(unsigned int allowedKeys, bool multipleKeys, QWidget *parent)
0046     : QWidget(parent)
0047     , mOpenPGPBackend(nullptr)
0048     , mSMIMEBackend(nullptr)
0049     , mMulti(multipleKeys)
0050     , mKeyUsage(allowedKeys)
0051     , mJobs(0)
0052     , d(nullptr)
0053 {
0054     init();
0055 }
0056 
0057 Kleo::KeyRequester::KeyRequester(QWidget *parent)
0058     : QWidget(parent)
0059     , mOpenPGPBackend(nullptr)
0060     , mSMIMEBackend(nullptr)
0061     , mMulti(false)
0062     , mKeyUsage(0)
0063     , mJobs(0)
0064     , d(nullptr)
0065 {
0066     init();
0067 }
0068 
0069 void Kleo::KeyRequester::init()
0070 {
0071     auto hlay = new QHBoxLayout(this);
0072     hlay->setContentsMargins(0, 0, 0, 0);
0073 
0074     if (DeVSCompliance::isCompliant()) {
0075         mComplianceIcon = new QLabel{this};
0076         mComplianceIcon->setPixmap(Formatting::questionIcon().pixmap(22));
0077     }
0078 
0079     // the label where the key id is to be displayed:
0080     mLabel = new QLabel(this);
0081     mLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
0082 
0083     // the button to unset any key:
0084     mEraseButton = new QPushButton(this);
0085     mEraseButton->setAutoDefault(false);
0086     mEraseButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
0087     mEraseButton->setIcon(
0088         QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl")));
0089     mEraseButton->setToolTip(i18n("Clear"));
0090 
0091     // the button to call the KeySelectionDialog:
0092     mDialogButton = new QPushButton(i18n("Change..."), this);
0093     mDialogButton->setAutoDefault(false);
0094 
0095     if (mComplianceIcon) {
0096         hlay->addWidget(mComplianceIcon);
0097     }
0098     hlay->addWidget(mLabel, 1);
0099     hlay->addWidget(mEraseButton);
0100     hlay->addWidget(mDialogButton);
0101 
0102     connect(mEraseButton, &QPushButton::clicked, this, &SigningKeyRequester::slotEraseButtonClicked);
0103     connect(mDialogButton, &QPushButton::clicked, this, &SigningKeyRequester::slotDialogButtonClicked);
0104 
0105     setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
0106 
0107     setAllowedKeys(mKeyUsage);
0108 }
0109 
0110 Kleo::KeyRequester::~KeyRequester()
0111 {
0112 }
0113 
0114 const std::vector<GpgME::Key> &Kleo::KeyRequester::keys() const
0115 {
0116     return mKeys;
0117 }
0118 
0119 const GpgME::Key &Kleo::KeyRequester::key() const
0120 {
0121     static const GpgME::Key null = GpgME::Key::null;
0122     if (mKeys.empty()) {
0123         return null;
0124     } else {
0125         return mKeys.front();
0126     }
0127 }
0128 
0129 void Kleo::KeyRequester::setKeys(const std::vector<GpgME::Key> &keys)
0130 {
0131     mKeys.clear();
0132     for (auto it = keys.begin(); it != keys.end(); ++it) {
0133         if (!it->isNull()) {
0134             mKeys.push_back(*it);
0135         }
0136     }
0137     updateKeys();
0138 }
0139 
0140 void Kleo::KeyRequester::setKey(const GpgME::Key &key)
0141 {
0142     mKeys.clear();
0143     if (!key.isNull()) {
0144         mKeys.push_back(key);
0145     }
0146     updateKeys();
0147 }
0148 
0149 QString Kleo::KeyRequester::fingerprint() const
0150 {
0151     if (mKeys.empty()) {
0152         return QString();
0153     } else {
0154         return QLatin1StringView(mKeys.front().primaryFingerprint());
0155     }
0156 }
0157 
0158 QStringList Kleo::KeyRequester::fingerprints() const
0159 {
0160     QStringList result;
0161     for (auto it = mKeys.begin(); it != mKeys.end(); ++it) {
0162         if (!it->isNull()) {
0163             if (const char *fpr = it->primaryFingerprint()) {
0164                 result.push_back(QLatin1StringView(fpr));
0165             }
0166         }
0167     }
0168     return result;
0169 }
0170 
0171 void Kleo::KeyRequester::setFingerprint(const QString &fingerprint)
0172 {
0173     startKeyListJob(QStringList(fingerprint));
0174 }
0175 
0176 void Kleo::KeyRequester::setFingerprints(const QStringList &fingerprints)
0177 {
0178     startKeyListJob(fingerprints);
0179 }
0180 
0181 void Kleo::KeyRequester::updateKeys()
0182 {
0183     if (mKeys.empty()) {
0184         if (mComplianceIcon) {
0185             mComplianceIcon->setPixmap(Formatting::unavailableIcon().pixmap(22));
0186             mComplianceIcon->setToolTip(QString{});
0187         }
0188         mLabel->clear();
0189         return;
0190     }
0191     if (mKeys.size() > 1) {
0192         setMultipleKeysEnabled(true);
0193     }
0194 
0195     QStringList labelTexts;
0196     QString toolTipText;
0197     for (std::vector<GpgME::Key>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) {
0198         if (it->isNull()) {
0199             continue;
0200         }
0201         const QString fpr = QLatin1StringView(it->primaryFingerprint());
0202         labelTexts.push_back(fpr.right(8));
0203         toolTipText += fpr.right(8) + QLatin1StringView(": ");
0204         if (const char *uid = it->userID(0).id()) {
0205             if (it->protocol() == GpgME::OpenPGP) {
0206                 toolTipText += QString::fromUtf8(uid);
0207             } else {
0208                 toolTipText += Kleo::DN(uid).prettyDN();
0209             }
0210         } else {
0211             toolTipText += xi18n("<placeholder>unknown</placeholder>");
0212         }
0213         toolTipText += QLatin1Char('\n');
0214     }
0215     if (mComplianceIcon) {
0216         if (Kleo::all_of(mKeys, &Kleo::DeVSCompliance::keyIsCompliant)) {
0217             mComplianceIcon->setPixmap(Formatting::successIcon().pixmap(22));
0218             mComplianceIcon->setToolTip(DeVSCompliance::name(true));
0219         } else {
0220             mComplianceIcon->setPixmap(Formatting::warningIcon().pixmap(22));
0221             mComplianceIcon->setToolTip(DeVSCompliance::name(false));
0222         }
0223     }
0224     mLabel->setText(labelTexts.join(QLatin1StringView(", ")));
0225     mLabel->setToolTip(toolTipText);
0226 }
0227 
0228 #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
0229 #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
0230 static void showKeyListError(QWidget *parent, const GpgME::Error &err)
0231 {
0232     Q_ASSERT(err);
0233     const QString msg = i18n(
0234         "<qt><p>An error occurred while fetching "
0235         "the keys from the backend:</p>"
0236         "<p><b>%1</b></p></qt>",
0237         Formatting::errorAsString(err));
0238 
0239     KMessageBox::error(parent, msg, i18n("Key Listing Failed"));
0240 }
0241 #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
0242 
0243 void Kleo::KeyRequester::startKeyListJob(const QStringList &fingerprints)
0244 {
0245     if (!mSMIMEBackend && !mOpenPGPBackend) {
0246         return;
0247     }
0248 
0249     mTmpKeys.clear();
0250     mJobs = 0;
0251 
0252     unsigned int count = 0;
0253     for (QStringList::const_iterator it = fingerprints.begin(); it != fingerprints.end(); ++it) {
0254         if (!(*it).trimmed().isEmpty()) {
0255             ++count;
0256         }
0257     }
0258 
0259     if (!count) {
0260         // don't fall into the trap that an empty pattern means
0261         // "return all keys" :)
0262         setKey(GpgME::Key::null);
0263         return;
0264     }
0265 
0266     if (mOpenPGPBackend) {
0267         KeyListJob *job = mOpenPGPBackend->keyListJob(false); // local, no sigs
0268         if (!job) {
0269             KMessageBox::error(this,
0270                                i18n("The OpenPGP backend does not support listing keys. "
0271                                     "Check your installation."),
0272                                i18n("Key Listing Failed"));
0273         } else {
0274             connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
0275             connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
0276 
0277             const GpgME::Error err =
0278                 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
0279 
0280             if (err) {
0281                 showKeyListError(this, err);
0282             } else {
0283                 ++mJobs;
0284             }
0285         }
0286     }
0287 
0288     if (mSMIMEBackend) {
0289         KeyListJob *job = mSMIMEBackend->keyListJob(false); // local, no sigs
0290         if (!job) {
0291             KMessageBox::error(this,
0292                                i18n("The S/MIME backend does not support listing keys. "
0293                                     "Check your installation."),
0294                                i18n("Key Listing Failed"));
0295         } else {
0296             connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
0297             connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
0298 
0299             const GpgME::Error err =
0300                 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
0301 
0302             if (err) {
0303                 showKeyListError(this, err);
0304             } else {
0305                 ++mJobs;
0306             }
0307         }
0308     }
0309 
0310     if (mJobs > 0) {
0311         mEraseButton->setEnabled(false);
0312         mDialogButton->setEnabled(false);
0313     }
0314 }
0315 
0316 void Kleo::KeyRequester::slotNextKey(const GpgME::Key &key)
0317 {
0318     if (!key.isNull()) {
0319         mTmpKeys.push_back(key);
0320     }
0321 }
0322 
0323 void Kleo::KeyRequester::slotKeyListResult(const GpgME::KeyListResult &res)
0324 {
0325     if (res.error()) {
0326         showKeyListError(this, res.error());
0327     }
0328 
0329     if (--mJobs <= 0) {
0330         mEraseButton->setEnabled(true);
0331         mDialogButton->setEnabled(true);
0332 
0333         setKeys(mTmpKeys);
0334         mTmpKeys.clear();
0335     }
0336 }
0337 
0338 void Kleo::KeyRequester::slotDialogButtonClicked()
0339 {
0340     KeySelectionDialog *dlg = mKeys.empty() ? new KeySelectionDialog(mDialogCaption, mDialogMessage, mInitialQuery, mKeyUsage, mMulti, false, this)
0341                                             : new KeySelectionDialog(mDialogCaption, mDialogCaption, mKeys, mKeyUsage, mMulti, false, this);
0342 
0343     if (dlg->exec() == QDialog::Accepted) {
0344         if (mMulti) {
0345             setKeys(dlg->selectedKeys());
0346         } else {
0347             setKey(dlg->selectedKey());
0348         }
0349         Q_EMIT changed();
0350     }
0351 
0352     delete dlg;
0353 }
0354 
0355 void Kleo::KeyRequester::slotEraseButtonClicked()
0356 {
0357     if (!mKeys.empty()) {
0358         Q_EMIT changed();
0359     }
0360     mKeys.clear();
0361     updateKeys();
0362 }
0363 
0364 void Kleo::KeyRequester::setDialogCaption(const QString &caption)
0365 {
0366     mDialogCaption = caption;
0367 }
0368 
0369 void Kleo::KeyRequester::setDialogMessage(const QString &msg)
0370 {
0371     mDialogMessage = msg;
0372 }
0373 
0374 bool Kleo::KeyRequester::isMultipleKeysEnabled() const
0375 {
0376     return mMulti;
0377 }
0378 
0379 void Kleo::KeyRequester::setMultipleKeysEnabled(bool multi)
0380 {
0381     if (multi == mMulti) {
0382         return;
0383     }
0384 
0385     if (!multi && !mKeys.empty()) {
0386         mKeys.erase(mKeys.begin() + 1, mKeys.end());
0387     }
0388 
0389     mMulti = multi;
0390     updateKeys();
0391 }
0392 
0393 unsigned int Kleo::KeyRequester::allowedKeys() const
0394 {
0395     return mKeyUsage;
0396 }
0397 
0398 void Kleo::KeyRequester::setAllowedKeys(unsigned int keyUsage)
0399 {
0400     mKeyUsage = keyUsage;
0401     mOpenPGPBackend = nullptr;
0402     mSMIMEBackend = nullptr;
0403 
0404     if (mKeyUsage & KeySelectionDialog::OpenPGPKeys) {
0405         mOpenPGPBackend = openpgp();
0406     }
0407     if (mKeyUsage & KeySelectionDialog::SMIMEKeys) {
0408         mSMIMEBackend = smime();
0409     }
0410 
0411     if (mOpenPGPBackend && !mSMIMEBackend) {
0412         mDialogCaption = i18n("OpenPGP Key Selection");
0413         mDialogMessage = i18n("Please select an OpenPGP key to use.");
0414     } else if (!mOpenPGPBackend && mSMIMEBackend) {
0415         mDialogCaption = i18n("S/MIME Key Selection");
0416         mDialogMessage = i18n("Please select an S/MIME key to use.");
0417     } else {
0418         mDialogCaption = i18n("Key Selection");
0419         mDialogMessage = i18n("Please select an (OpenPGP or S/MIME) key to use.");
0420     }
0421 }
0422 
0423 QPushButton *Kleo::KeyRequester::dialogButton()
0424 {
0425     return mDialogButton;
0426 }
0427 
0428 QPushButton *Kleo::KeyRequester::eraseButton()
0429 {
0430     return mEraseButton;
0431 }
0432 
0433 static inline unsigned int foo(bool openpgp, bool smime, bool trusted, bool valid)
0434 {
0435     unsigned int result = 0;
0436     if (openpgp) {
0437         result |= Kleo::KeySelectionDialog::OpenPGPKeys;
0438     }
0439     if (smime) {
0440         result |= Kleo::KeySelectionDialog::SMIMEKeys;
0441     }
0442     if (trusted) {
0443         result |= Kleo::KeySelectionDialog::TrustedKeys;
0444     }
0445     if (valid) {
0446         result |= Kleo::KeySelectionDialog::ValidKeys;
0447     }
0448     return result;
0449 }
0450 
0451 static inline unsigned int encryptionKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
0452 {
0453     return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::EncryptionKeys | Kleo::KeySelectionDialog::PublicKeys;
0454 }
0455 
0456 static inline unsigned int signingKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
0457 {
0458     return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::SigningKeys | Kleo::KeySelectionDialog::SecretKeys;
0459 }
0460 
0461 Kleo::EncryptionKeyRequester::EncryptionKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
0462     : KeyRequester(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
0463     , d(nullptr)
0464 {
0465 }
0466 
0467 Kleo::EncryptionKeyRequester::EncryptionKeyRequester(QWidget *parent)
0468     : KeyRequester(0, false, parent)
0469     , d(nullptr)
0470 {
0471 }
0472 
0473 Kleo::EncryptionKeyRequester::~EncryptionKeyRequester()
0474 {
0475 }
0476 
0477 void Kleo::EncryptionKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
0478 {
0479     KeyRequester::setAllowedKeys(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
0480 }
0481 
0482 Kleo::SigningKeyRequester::SigningKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
0483     : KeyRequester(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
0484     , d(nullptr)
0485 {
0486 }
0487 
0488 Kleo::SigningKeyRequester::SigningKeyRequester(QWidget *parent)
0489     : KeyRequester(0, false, parent)
0490     , d(nullptr)
0491 {
0492 }
0493 
0494 Kleo::SigningKeyRequester::~SigningKeyRequester()
0495 {
0496 }
0497 
0498 void Kleo::SigningKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
0499 {
0500     KeyRequester::setAllowedKeys(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
0501 }
0502 
0503 void Kleo::KeyRequester::virtual_hook(int, void *)
0504 {
0505 }
0506 void Kleo::EncryptionKeyRequester::virtual_hook(int id, void *data)
0507 {
0508     KeyRequester::virtual_hook(id, data);
0509 }
0510 void Kleo::SigningKeyRequester::virtual_hook(int id, void *data)
0511 {
0512     KeyRequester::virtual_hook(id, data);
0513 }
0514 
0515 #include "moc_keyrequester.cpp"