diff --git a/src/ui/keyrequester.cpp b/src/ui/keyrequester.cpp index d2d8e9a7b..30f5abee5 100644 --- a/src/ui/keyrequester.cpp +++ b/src/ui/keyrequester.cpp @@ -1,488 +1,519 @@ /* -*- c++ -*- keyrequester.cpp This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB Based on kpgpui.cpp SPDX-FileCopyrightText: 2001, 2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details This file is part of KPGP, the KDE PGP/GnuPG support library. SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "keyrequester.h" #include "keyselectiondialog.h" +#include +#include #include +#include #include #include #include #include #include #include #include #include #include #include using namespace QGpgME; +using namespace Kleo; Kleo::KeyRequester::KeyRequester(unsigned int allowedKeys, bool multipleKeys, QWidget *parent) : QWidget(parent) , mOpenPGPBackend(nullptr) , mSMIMEBackend(nullptr) , mMulti(multipleKeys) , mKeyUsage(allowedKeys) , mJobs(0) , d(nullptr) { init(); } Kleo::KeyRequester::KeyRequester(QWidget *parent) : QWidget(parent) , mOpenPGPBackend(nullptr) , mSMIMEBackend(nullptr) , mMulti(false) , mKeyUsage(0) , mJobs(0) , d(nullptr) { init(); } void Kleo::KeyRequester::init() { auto hlay = new QHBoxLayout(this); hlay->setContentsMargins(0, 0, 0, 0); + if (DeVSCompliance::isCompliant()) { + mComplianceIcon = new QLabel{this}; + mComplianceIcon->setPixmap(QIcon::fromTheme(QStringLiteral("emblem-question")).pixmap(22)); + } + // the label where the key id is to be displayed: mLabel = new QLabel(this); mLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); // the button to unset any key: mEraseButton = new QPushButton(this); mEraseButton->setAutoDefault(false); mEraseButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); mEraseButton->setIcon( QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl"))); mEraseButton->setToolTip(i18n("Clear")); // the button to call the KeySelectionDialog: mDialogButton = new QPushButton(i18n("Change..."), this); mDialogButton->setAutoDefault(false); + if (mComplianceIcon) { + hlay->addWidget(mComplianceIcon); + } hlay->addWidget(mLabel, 1); hlay->addWidget(mEraseButton); hlay->addWidget(mDialogButton); connect(mEraseButton, &QPushButton::clicked, this, &SigningKeyRequester::slotEraseButtonClicked); connect(mDialogButton, &QPushButton::clicked, this, &SigningKeyRequester::slotDialogButtonClicked); setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); setAllowedKeys(mKeyUsage); } Kleo::KeyRequester::~KeyRequester() { } const std::vector &Kleo::KeyRequester::keys() const { return mKeys; } const GpgME::Key &Kleo::KeyRequester::key() const { static const GpgME::Key null = GpgME::Key::null; if (mKeys.empty()) { return null; } else { return mKeys.front(); } } void Kleo::KeyRequester::setKeys(const std::vector &keys) { mKeys.clear(); for (auto it = keys.begin(); it != keys.end(); ++it) { if (!it->isNull()) { mKeys.push_back(*it); } } updateKeys(); } void Kleo::KeyRequester::setKey(const GpgME::Key &key) { mKeys.clear(); if (!key.isNull()) { mKeys.push_back(key); } updateKeys(); } QString Kleo::KeyRequester::fingerprint() const { if (mKeys.empty()) { return QString(); } else { return QLatin1String(mKeys.front().primaryFingerprint()); } } QStringList Kleo::KeyRequester::fingerprints() const { QStringList result; for (auto it = mKeys.begin(); it != mKeys.end(); ++it) { if (!it->isNull()) { if (const char *fpr = it->primaryFingerprint()) { result.push_back(QLatin1String(fpr)); } } } return result; } void Kleo::KeyRequester::setFingerprint(const QString &fingerprint) { startKeyListJob(QStringList(fingerprint)); } void Kleo::KeyRequester::setFingerprints(const QStringList &fingerprints) { startKeyListJob(fingerprints); } +static bool keyIsCompliant(const GpgME::Key &key) +{ + return (key.keyListMode() & GpgME::Validate) // + && Formatting::uidsHaveFullValidity(key) // + && Formatting::isKeyDeVs(key); +} + void Kleo::KeyRequester::updateKeys() { if (mKeys.empty()) { + if (mComplianceIcon) { + mComplianceIcon->setPixmap(QIcon::fromTheme(QStringLiteral("emblem-unavailable")).pixmap(22)); + mComplianceIcon->setToolTip(QString{}); + } mLabel->clear(); return; } if (mKeys.size() > 1) { setMultipleKeysEnabled(true); } QStringList labelTexts; QString toolTipText; for (std::vector::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) { if (it->isNull()) { continue; } const QString fpr = QLatin1String(it->primaryFingerprint()); labelTexts.push_back(fpr.right(8)); toolTipText += fpr.right(8) + QLatin1String(": "); if (const char *uid = it->userID(0).id()) { if (it->protocol() == GpgME::OpenPGP) { toolTipText += QString::fromUtf8(uid); } else { toolTipText += Kleo::DN(uid).prettyDN(); } } else { toolTipText += xi18n("unknown"); } toolTipText += QLatin1Char('\n'); } - + if (mComplianceIcon) { + if (Kleo::all_of(mKeys, &keyIsCompliant)) { + mComplianceIcon->setPixmap(QIcon::fromTheme(QStringLiteral("emblem-success")).pixmap(22)); + mComplianceIcon->setToolTip(DeVSCompliance::name(true)); + } else { + mComplianceIcon->setPixmap(QIcon::fromTheme(QStringLiteral("emblem-information")).pixmap(22)); + mComplianceIcon->setToolTip(DeVSCompliance::name(false)); + } + } mLabel->setText(labelTexts.join(QLatin1String(", "))); mLabel->setToolTip(toolTipText); } #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ static void showKeyListError(QWidget *parent, const GpgME::Error &err) { Q_ASSERT(err); const QString msg = i18n( "

An error occurred while fetching " "the keys from the backend:

" "

%1

", QString::fromLocal8Bit(err.asString())); KMessageBox::error(parent, msg, i18n("Key Listing Failed")); } #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__ void Kleo::KeyRequester::startKeyListJob(const QStringList &fingerprints) { if (!mSMIMEBackend && !mOpenPGPBackend) { return; } mTmpKeys.clear(); mJobs = 0; unsigned int count = 0; for (QStringList::const_iterator it = fingerprints.begin(); it != fingerprints.end(); ++it) { if (!(*it).trimmed().isEmpty()) { ++count; } } if (!count) { // don't fall into the trap that an empty pattern means // "return all keys" :) setKey(GpgME::Key::null); return; } if (mOpenPGPBackend) { KeyListJob *job = mOpenPGPBackend->keyListJob(false); // local, no sigs if (!job) { KMessageBox::error(this, i18n("The OpenPGP backend does not support listing keys. " "Check your installation."), i18n("Key Listing Failed")); } else { connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult); connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey); const GpgME::Error err = job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys)); if (err) { showKeyListError(this, err); } else { ++mJobs; } } } if (mSMIMEBackend) { KeyListJob *job = mSMIMEBackend->keyListJob(false); // local, no sigs if (!job) { KMessageBox::error(this, i18n("The S/MIME backend does not support listing keys. " "Check your installation."), i18n("Key Listing Failed")); } else { connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult); connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey); const GpgME::Error err = job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys)); if (err) { showKeyListError(this, err); } else { ++mJobs; } } } if (mJobs > 0) { mEraseButton->setEnabled(false); mDialogButton->setEnabled(false); } } void Kleo::KeyRequester::slotNextKey(const GpgME::Key &key) { if (!key.isNull()) { mTmpKeys.push_back(key); } } void Kleo::KeyRequester::slotKeyListResult(const GpgME::KeyListResult &res) { if (res.error()) { showKeyListError(this, res.error()); } if (--mJobs <= 0) { mEraseButton->setEnabled(true); mDialogButton->setEnabled(true); setKeys(mTmpKeys); mTmpKeys.clear(); } } void Kleo::KeyRequester::slotDialogButtonClicked() { KeySelectionDialog *dlg = mKeys.empty() ? new KeySelectionDialog(mDialogCaption, mDialogMessage, mInitialQuery, mKeyUsage, mMulti, false, this) : new KeySelectionDialog(mDialogCaption, mDialogCaption, mKeys, mKeyUsage, mMulti, false, this); if (dlg->exec() == QDialog::Accepted) { if (mMulti) { setKeys(dlg->selectedKeys()); } else { setKey(dlg->selectedKey()); } Q_EMIT changed(); } delete dlg; } void Kleo::KeyRequester::slotEraseButtonClicked() { if (!mKeys.empty()) { Q_EMIT changed(); } mKeys.clear(); updateKeys(); } void Kleo::KeyRequester::setDialogCaption(const QString &caption) { mDialogCaption = caption; } void Kleo::KeyRequester::setDialogMessage(const QString &msg) { mDialogMessage = msg; } bool Kleo::KeyRequester::isMultipleKeysEnabled() const { return mMulti; } void Kleo::KeyRequester::setMultipleKeysEnabled(bool multi) { if (multi == mMulti) { return; } if (!multi && !mKeys.empty()) { mKeys.erase(mKeys.begin() + 1, mKeys.end()); } mMulti = multi; updateKeys(); } unsigned int Kleo::KeyRequester::allowedKeys() const { return mKeyUsage; } void Kleo::KeyRequester::setAllowedKeys(unsigned int keyUsage) { mKeyUsage = keyUsage; mOpenPGPBackend = nullptr; mSMIMEBackend = nullptr; if (mKeyUsage & KeySelectionDialog::OpenPGPKeys) { mOpenPGPBackend = openpgp(); } if (mKeyUsage & KeySelectionDialog::SMIMEKeys) { mSMIMEBackend = smime(); } if (mOpenPGPBackend && !mSMIMEBackend) { mDialogCaption = i18n("OpenPGP Key Selection"); mDialogMessage = i18n("Please select an OpenPGP key to use."); } else if (!mOpenPGPBackend && mSMIMEBackend) { mDialogCaption = i18n("S/MIME Key Selection"); mDialogMessage = i18n("Please select an S/MIME key to use."); } else { mDialogCaption = i18n("Key Selection"); mDialogMessage = i18n("Please select an (OpenPGP or S/MIME) key to use."); } } QPushButton *Kleo::KeyRequester::dialogButton() { return mDialogButton; } QPushButton *Kleo::KeyRequester::eraseButton() { return mEraseButton; } static inline unsigned int foo(bool openpgp, bool smime, bool trusted, bool valid) { unsigned int result = 0; if (openpgp) { result |= Kleo::KeySelectionDialog::OpenPGPKeys; } if (smime) { result |= Kleo::KeySelectionDialog::SMIMEKeys; } if (trusted) { result |= Kleo::KeySelectionDialog::TrustedKeys; } if (valid) { result |= Kleo::KeySelectionDialog::ValidKeys; } return result; } static inline unsigned int encryptionKeyUsage(bool openpgp, bool smime, bool trusted, bool valid) { return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::EncryptionKeys | Kleo::KeySelectionDialog::PublicKeys; } static inline unsigned int signingKeyUsage(bool openpgp, bool smime, bool trusted, bool valid) { return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::SigningKeys | Kleo::KeySelectionDialog::SecretKeys; } Kleo::EncryptionKeyRequester::EncryptionKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid) : KeyRequester(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent) , d(nullptr) { } Kleo::EncryptionKeyRequester::EncryptionKeyRequester(QWidget *parent) : KeyRequester(0, false, parent) , d(nullptr) { } Kleo::EncryptionKeyRequester::~EncryptionKeyRequester() { } void Kleo::EncryptionKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid) { KeyRequester::setAllowedKeys(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid)); } Kleo::SigningKeyRequester::SigningKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid) : KeyRequester(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent) , d(nullptr) { } Kleo::SigningKeyRequester::SigningKeyRequester(QWidget *parent) : KeyRequester(0, false, parent) , d(nullptr) { } Kleo::SigningKeyRequester::~SigningKeyRequester() { } void Kleo::SigningKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid) { KeyRequester::setAllowedKeys(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid)); } void Kleo::KeyRequester::virtual_hook(int, void *) { } void Kleo::EncryptionKeyRequester::virtual_hook(int id, void *data) { KeyRequester::virtual_hook(id, data); } void Kleo::SigningKeyRequester::virtual_hook(int id, void *data) { KeyRequester::virtual_hook(id, data); } diff --git a/src/ui/keyrequester.h b/src/ui/keyrequester.h index 26d902825..3b26a3d1f 100644 --- a/src/ui/keyrequester.h +++ b/src/ui/keyrequester.h @@ -1,203 +1,204 @@ /* -*- c++ -*- keyrequester.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB Based on kpgpui.h SPDX-FileCopyrightText: 2001, 2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details This file is part of KPGP, the KDE PGP/GnuPG support library. SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include #include namespace GpgME { class Key; class KeyListResult; } class QString; class QPushButton; namespace Kleo { /// Base class for SigningKeyRequester and EncryptionKeyRequester class KLEO_EXPORT KeyRequester : public QWidget { Q_OBJECT public: explicit KeyRequester(unsigned int allowedKeys, bool multipleKeys = false, QWidget *parent = nullptr); // Constructor for Qt Designer explicit KeyRequester(QWidget *parent = nullptr); ~KeyRequester() override; const GpgME::Key &key() const; /** Preferred method to set a key for non-multi-KeyRequesters. Doesn't start a backend KeyListJob. */ void setKey(const GpgME::Key &key); const std::vector &keys() const; /** Preferred method to set a key for multi-KeyRequesters. Doesn't start a backend KeyListJob. */ void setKeys(const std::vector &keys); QString fingerprint() const; /** Set the key by fingerprint. Starts a background KeyListJob to retrieve the complete GpgME::Key object */ void setFingerprint(const QString &fingerprint); QStringList fingerprints() const; /** Set the keys by fingerprint. Starts a background KeyListJob to retrieve the complete GpgME::Key objects */ void setFingerprints(const QStringList &fingerprints); QPushButton *eraseButton(); QPushButton *dialogButton(); void setDialogCaption(const QString &caption); void setDialogMessage(const QString &message); bool isMultipleKeysEnabled() const; void setMultipleKeysEnabled(bool enable); unsigned int allowedKeys() const; void setAllowedKeys(unsigned int allowed); void setInitialQuery(const QString &s) { mInitialQuery = s; } const QString &initialQuery() const { return mInitialQuery; } Q_SIGNALS: void changed(); private: void init(); void startKeyListJob(const QStringList &fingerprints); void updateKeys(); private Q_SLOTS: void slotNextKey(const GpgME::Key &key); void slotKeyListResult(const GpgME::KeyListResult &result); void slotDialogButtonClicked(); void slotEraseButtonClicked(); private: const QGpgME::Protocol *mOpenPGPBackend = nullptr; const QGpgME::Protocol *mSMIMEBackend = nullptr; + QLabel *mComplianceIcon = nullptr; QLabel *mLabel = nullptr; QPushButton *mEraseButton = nullptr; QPushButton *mDialogButton = nullptr; QString mDialogCaption, mDialogMessage, mInitialQuery; bool mMulti; unsigned int mKeyUsage; int mJobs; std::vector mKeys; std::vector mTmpKeys; private: class Private; Private *const d; protected: virtual void virtual_hook(int, void *); }; class KLEO_EXPORT EncryptionKeyRequester : public KeyRequester { Q_OBJECT public: enum { OpenPGP = 1, SMIME = 2, AllProtocols = OpenPGP | SMIME }; /** * Preferred constructor */ explicit EncryptionKeyRequester(bool multipleKeys = false, unsigned int proto = AllProtocols, QWidget *parent = nullptr, bool onlyTrusted = true, bool onlyValid = true); /** * Constructor for Qt designer */ explicit EncryptionKeyRequester(QWidget *parent); ~EncryptionKeyRequester() override; void setAllowedKeys(unsigned int proto, bool onlyTrusted = true, bool onlyValid = true); private: class Private; Private *const d; protected: void virtual_hook(int, void *) override; }; class KLEO_EXPORT SigningKeyRequester : public KeyRequester { Q_OBJECT public: enum { OpenPGP = 1, SMIME = 2, AllProtocols = OpenPGP | SMIME }; /** * Preferred constructor * @param multipleKeys whether multiple keys can be selected * * @param proto the allowed protocols, OpenPGP and/or SMIME * @param parent the parent widget * @param onlyTrusted only show trusted keys * @param onlyValid only show valid keys */ explicit SigningKeyRequester(bool multipleKeys = false, unsigned int proto = AllProtocols, QWidget *parent = nullptr, bool onlyTrusted = true, bool onlyValid = true); /** * Constructor for Qt designer */ explicit SigningKeyRequester(QWidget *parent); ~SigningKeyRequester() override; /* * Those parameters affect the parameters given to the key selection dialog. * @param proto the allowed protocols, OpenPGP and/or SMIME * @param onlyTrusted only show trusted keys * @param onlyValid only show valid keys */ void setAllowedKeys(unsigned int proto, bool onlyTrusted = true, bool onlyValid = true); private: class Private; Private *const d; protected: void virtual_hook(int, void *) override; }; }