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"