Libkleo

keyrequester.cpp
1/* -*- c++ -*-
2 keyrequester.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6
7 Based on kpgpui.cpp
8 SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
9 See file libkdenetwork/AUTHORS.kpgp for details
10
11 This file is part of KPGP, the KDE PGP/GnuPG support library.
12
13 SPDX-License-Identifier: GPL-2.0-or-later
14 */
15
16#include <config-libkleo.h>
17
18#include "keyrequester.h"
19
20#include "keyselectiondialog.h"
21
22#include <libkleo/algorithm.h>
23#include <libkleo/compliance.h>
24#include <libkleo/dn.h>
25#include <libkleo/formatting.h>
26#include <libkleo/keyhelpers.h>
27
28#include <KLocalizedString>
29#include <KMessageBox>
30
31#include <QGpgME/KeyListJob>
32
33#include <QApplication>
34#include <QDialog>
35#include <QHBoxLayout>
36#include <QPushButton>
37#include <QString>
38
39#include <gpgme++/key.h>
40#include <gpgme++/keylistresult.h>
41
42using namespace QGpgME;
43using namespace Kleo;
44
45Kleo::KeyRequester::KeyRequester(unsigned int allowedKeys, bool multipleKeys, QWidget *parent)
46 : QWidget(parent)
47 , mOpenPGPBackend(nullptr)
48 , mSMIMEBackend(nullptr)
49 , mMulti(multipleKeys)
50 , mKeyUsage(allowedKeys)
51 , mJobs(0)
52 , d(nullptr)
53{
54 init();
55}
56
57Kleo::KeyRequester::KeyRequester(QWidget *parent)
58 : QWidget(parent)
59 , mOpenPGPBackend(nullptr)
60 , mSMIMEBackend(nullptr)
61 , mMulti(false)
62 , mKeyUsage(0)
63 , mJobs(0)
64 , d(nullptr)
65{
66 init();
67}
68
69void Kleo::KeyRequester::init()
70{
71 auto hlay = new QHBoxLayout(this);
72 hlay->setContentsMargins(0, 0, 0, 0);
73
74 if (DeVSCompliance::isCompliant()) {
75 mComplianceIcon = new QLabel{this};
76 mComplianceIcon->setPixmap(Formatting::questionIcon().pixmap(22));
77 }
78
79 // the label where the key id is to be displayed:
80 mLabel = new QLabel(this);
82
83 // the button to unset any key:
84 mEraseButton = new QPushButton(this);
85 mEraseButton->setAutoDefault(false);
87 mEraseButton->setIcon(
88 QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl")));
89 mEraseButton->setToolTip(i18nc("@info:tooltip", "Clear"));
90
91 // the button to call the KeySelectionDialog:
92 mDialogButton = new QPushButton(i18n("Change..."), this);
93 mDialogButton->setAutoDefault(false);
94
95 if (mComplianceIcon) {
96 hlay->addWidget(mComplianceIcon);
97 }
98 hlay->addWidget(mLabel, 1);
99 hlay->addWidget(mEraseButton);
100 hlay->addWidget(mDialogButton);
101
102 connect(mEraseButton, &QPushButton::clicked, this, &SigningKeyRequester::slotEraseButtonClicked);
103 connect(mDialogButton, &QPushButton::clicked, this, &SigningKeyRequester::slotDialogButtonClicked);
104
106
107 setAllowedKeys(mKeyUsage);
108}
109
110Kleo::KeyRequester::~KeyRequester()
111{
112}
113
114const std::vector<GpgME::Key> &Kleo::KeyRequester::keys() const
115{
116 return mKeys;
117}
118
119const GpgME::Key &Kleo::KeyRequester::key() const
120{
121 static const GpgME::Key null = GpgME::Key::null;
122 if (mKeys.empty()) {
123 return null;
124 } else {
125 return mKeys.front();
126 }
127}
128
129void Kleo::KeyRequester::setKeys(const std::vector<GpgME::Key> &keys)
130{
131 mKeys.clear();
132 for (auto it = keys.begin(); it != keys.end(); ++it) {
133 if (!it->isNull()) {
134 mKeys.push_back(*it);
135 }
136 }
137 updateKeys();
138}
139
140void Kleo::KeyRequester::setKey(const GpgME::Key &key)
141{
142 mKeys.clear();
143 if (!key.isNull()) {
144 mKeys.push_back(key);
145 }
146 updateKeys();
147}
148
149QString Kleo::KeyRequester::fingerprint() const
150{
151 if (mKeys.empty()) {
152 return QString();
153 } else {
154 return QLatin1StringView(mKeys.front().primaryFingerprint());
155 }
156}
157
158QStringList Kleo::KeyRequester::fingerprints() const
159{
160 QStringList result;
161 for (auto it = mKeys.begin(); it != mKeys.end(); ++it) {
162 if (!it->isNull()) {
163 if (const char *fpr = it->primaryFingerprint()) {
164 result.push_back(QLatin1StringView(fpr));
165 }
166 }
167 }
168 return result;
169}
170
172{
173 startKeyListJob(QStringList(fingerprint));
174}
175
177{
178 startKeyListJob(fingerprints);
179}
180
181void Kleo::KeyRequester::updateKeys()
182{
183 if (mKeys.empty()) {
184 if (mComplianceIcon) {
185 mComplianceIcon->setPixmap(Formatting::unavailableIcon().pixmap(22));
186 mComplianceIcon->setToolTip(QString{});
187 }
188 mLabel->clear();
189 return;
190 }
191 if (mKeys.size() > 1) {
192 setMultipleKeysEnabled(true);
193 }
194
195 QStringList labelTexts;
196 QString toolTipText;
197 for (std::vector<GpgME::Key>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) {
198 if (it->isNull()) {
199 continue;
200 }
201 const QString fpr = QLatin1StringView(it->primaryFingerprint());
202 labelTexts.push_back(fpr.right(8));
203 toolTipText += fpr.right(8) + QLatin1StringView(": ");
204 if (const char *uid = it->userID(0).id()) {
205 if (it->protocol() == GpgME::OpenPGP) {
206 toolTipText += QString::fromUtf8(uid);
207 } else {
208 toolTipText += Kleo::DN(uid).prettyDN();
209 }
210 } else {
211 toolTipText += xi18n("<placeholder>unknown</placeholder>");
212 }
213 toolTipText += QLatin1Char('\n');
214 }
215 if (mComplianceIcon) {
216 if (Kleo::all_of(mKeys, &Kleo::DeVSCompliance::keyIsCompliant)) {
217 mComplianceIcon->setPixmap(Formatting::successIcon().pixmap(22));
218 mComplianceIcon->setToolTip(DeVSCompliance::name(true));
219 } else {
220 mComplianceIcon->setPixmap(Formatting::warningIcon().pixmap(22));
221 mComplianceIcon->setToolTip(DeVSCompliance::name(false));
222 }
223 }
224 mLabel->setText(labelTexts.join(QLatin1StringView(", ")));
225 mLabel->setToolTip(toolTipText);
226}
227
228#ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
229#define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
230static void showKeyListError(QWidget *parent, const GpgME::Error &err)
231{
232 Q_ASSERT(err);
233 const QString msg = i18n(
234 "<qt><p>An error occurred while fetching "
235 "the keys from the backend:</p>"
236 "<p><b>%1</b></p></qt>",
237 Formatting::errorAsString(err));
238
239 KMessageBox::error(parent, msg, i18n("Key Listing Failed"));
240}
241#endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
242
243void Kleo::KeyRequester::startKeyListJob(const QStringList &fingerprints)
244{
245 if (!mSMIMEBackend && !mOpenPGPBackend) {
246 return;
247 }
248
249 mTmpKeys.clear();
250 mJobs = 0;
251
252 unsigned int count = 0;
253 for (QStringList::const_iterator it = fingerprints.begin(); it != fingerprints.end(); ++it) {
254 if (!(*it).trimmed().isEmpty()) {
255 ++count;
256 }
257 }
258
259 if (!count) {
260 // don't fall into the trap that an empty pattern means
261 // "return all keys" :)
262 setKey(GpgME::Key::null);
263 return;
264 }
265
266 if (mOpenPGPBackend) {
267 KeyListJob *job = mOpenPGPBackend->keyListJob(false); // local, no sigs
268 if (!job) {
270 i18n("The OpenPGP backend does not support listing keys. "
271 "Check your installation."),
272 i18n("Key Listing Failed"));
273 } else {
274 connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
275 connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
276
277 const GpgME::Error err =
278 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
279
280 if (err) {
281 showKeyListError(this, err);
282 } else {
283 ++mJobs;
284 }
285 }
286 }
287
288 if (mSMIMEBackend) {
289 KeyListJob *job = mSMIMEBackend->keyListJob(false); // local, no sigs
290 if (!job) {
292 i18n("The S/MIME backend does not support listing keys. "
293 "Check your installation."),
294 i18n("Key Listing Failed"));
295 } else {
296 connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
297 connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
298
299 const GpgME::Error err =
300 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
301
302 if (err) {
303 showKeyListError(this, err);
304 } else {
305 ++mJobs;
306 }
307 }
308 }
309
310 if (mJobs > 0) {
311 mEraseButton->setEnabled(false);
312 mDialogButton->setEnabled(false);
313 }
314}
315
316void Kleo::KeyRequester::slotNextKey(const GpgME::Key &key)
317{
318 if (!key.isNull()) {
319 mTmpKeys.push_back(key);
320 }
321}
322
323void Kleo::KeyRequester::slotKeyListResult(const GpgME::KeyListResult &res)
324{
325 if (res.error()) {
326 showKeyListError(this, res.error());
327 }
328
329 if (--mJobs <= 0) {
330 mEraseButton->setEnabled(true);
331 mDialogButton->setEnabled(true);
332
333 setKeys(mTmpKeys);
334 mTmpKeys.clear();
335 }
336}
337
338void Kleo::KeyRequester::slotDialogButtonClicked()
339{
340 KeySelectionDialog *dlg = mKeys.empty() ? new KeySelectionDialog(mDialogCaption, mDialogMessage, mInitialQuery, mKeyUsage, mMulti, false, this)
341 : new KeySelectionDialog(mDialogCaption, mDialogCaption, mKeys, mKeyUsage, mMulti, false, this);
342
343 if (dlg->exec() == QDialog::Accepted) {
344 if (mMulti) {
345 setKeys(dlg->selectedKeys());
346 } else {
347 setKey(dlg->selectedKey());
348 }
349 Q_EMIT changed();
350 }
351
352 delete dlg;
353}
354
355void Kleo::KeyRequester::slotEraseButtonClicked()
356{
357 if (!mKeys.empty()) {
358 Q_EMIT changed();
359 }
360 mKeys.clear();
361 updateKeys();
362}
363
364void Kleo::KeyRequester::setDialogCaption(const QString &caption)
365{
366 mDialogCaption = caption;
367}
368
369void Kleo::KeyRequester::setDialogMessage(const QString &msg)
370{
371 mDialogMessage = msg;
372}
373
374bool Kleo::KeyRequester::isMultipleKeysEnabled() const
375{
376 return mMulti;
377}
378
379void Kleo::KeyRequester::setMultipleKeysEnabled(bool multi)
380{
381 if (multi == mMulti) {
382 return;
383 }
384
385 if (!multi && !mKeys.empty()) {
386 mKeys.erase(mKeys.begin() + 1, mKeys.end());
387 }
388
389 mMulti = multi;
390 updateKeys();
391}
392
393unsigned int Kleo::KeyRequester::allowedKeys() const
394{
395 return mKeyUsage;
396}
397
398void Kleo::KeyRequester::setAllowedKeys(unsigned int keyUsage)
399{
400 mKeyUsage = keyUsage;
401 mOpenPGPBackend = nullptr;
402 mSMIMEBackend = nullptr;
403
404 if (mKeyUsage & KeySelectionDialog::OpenPGPKeys) {
405 mOpenPGPBackend = openpgp();
406 }
407 if (mKeyUsage & KeySelectionDialog::SMIMEKeys) {
408 mSMIMEBackend = smime();
409 }
410
411 if (mOpenPGPBackend && !mSMIMEBackend) {
412 mDialogCaption = i18n("OpenPGP Key Selection");
413 mDialogMessage = i18n("Please select an OpenPGP key to use.");
414 } else if (!mOpenPGPBackend && mSMIMEBackend) {
415 mDialogCaption = i18n("S/MIME Key Selection");
416 mDialogMessage = i18n("Please select an S/MIME key to use.");
417 } else {
418 mDialogCaption = i18n("Key Selection");
419 mDialogMessage = i18n("Please select an (OpenPGP or S/MIME) key to use.");
420 }
421}
422
423QPushButton *Kleo::KeyRequester::dialogButton()
424{
425 return mDialogButton;
426}
427
428QPushButton *Kleo::KeyRequester::eraseButton()
429{
430 return mEraseButton;
431}
432
433static inline unsigned int foo(bool openpgp, bool smime, bool trusted, bool valid)
434{
435 unsigned int result = 0;
436 if (openpgp) {
437 result |= Kleo::KeySelectionDialog::OpenPGPKeys;
438 }
439 if (smime) {
440 result |= Kleo::KeySelectionDialog::SMIMEKeys;
441 }
442 if (trusted) {
443 result |= Kleo::KeySelectionDialog::TrustedKeys;
444 }
445 if (valid) {
446 result |= Kleo::KeySelectionDialog::ValidKeys;
447 }
448 return result;
449}
450
451static inline unsigned int encryptionKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
452{
453 return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::EncryptionKeys | Kleo::KeySelectionDialog::PublicKeys;
454}
455
456static inline unsigned int signingKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
457{
458 return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::SigningKeys | Kleo::KeySelectionDialog::SecretKeys;
459}
460
461Kleo::EncryptionKeyRequester::EncryptionKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
462 : KeyRequester(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
463 , d(nullptr)
464{
465}
466
467Kleo::EncryptionKeyRequester::EncryptionKeyRequester(QWidget *parent)
468 : KeyRequester(0, false, parent)
469 , d(nullptr)
470{
471}
472
473Kleo::EncryptionKeyRequester::~EncryptionKeyRequester()
474{
475}
476
477void Kleo::EncryptionKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
478{
479 KeyRequester::setAllowedKeys(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
480}
481
482Kleo::SigningKeyRequester::SigningKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
483 : KeyRequester(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
484 , d(nullptr)
485{
486}
487
488Kleo::SigningKeyRequester::SigningKeyRequester(QWidget *parent)
489 : KeyRequester(0, false, parent)
490 , d(nullptr)
491{
492}
493
494Kleo::SigningKeyRequester::~SigningKeyRequester()
495{
496}
497
498void Kleo::SigningKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
499{
500 KeyRequester::setAllowedKeys(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
501}
502
503void Kleo::KeyRequester::virtual_hook(int, void *)
504{
505}
506void Kleo::EncryptionKeyRequester::virtual_hook(int id, void *data)
507{
508 KeyRequester::virtual_hook(id, data);
509}
510void Kleo::SigningKeyRequester::virtual_hook(int id, void *data)
511{
512 KeyRequester::virtual_hook(id, data);
513}
514
515#include "moc_keyrequester.cpp"
DN parser and reorderer.
Definition dn.h:27
QString prettyDN() const
Definition dn.cpp:439
Base class for SigningKeyRequester and EncryptionKeyRequester.
void setFingerprint(const QString &fingerprint)
Set the key by fingerprint.
void setFingerprints(const QStringList &fingerprints)
Set the keys by fingerprint.
void setKeys(const std::vector< GpgME::Key > &keys)
Preferred method to set a key for multi-KeyRequesters.
void setKey(const GpgME::Key &key)
Preferred method to set a key for non-multi-KeyRequesters.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString xi18n(const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void clicked(bool checked)
void setIcon(const QIcon &icon)
virtual int exec()
void setFrameStyle(int style)
bool isRightToLeft()
QIcon fromTheme(const QString &name)
void setPixmap(const QPixmap &)
iterator begin()
iterator end()
void push_back(parameter_type value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setAutoDefault(bool)
QString fromUtf8(QByteArrayView str)
QString right(qsizetype n) const const
QString join(QChar separator) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setSizePolicy(QSizePolicy)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:51:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.