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 
42 using namespace QGpgME;
43 using namespace Kleo;
44 
45 Kleo::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 
57 Kleo::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 
69 void 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);
81  mLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
82 
83  // the button to unset any key:
84  mEraseButton = new QPushButton(this);
85  mEraseButton->setAutoDefault(false);
86  mEraseButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
87  mEraseButton->setIcon(
88  QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl")));
89  mEraseButton->setToolTip(i18n("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 
110 Kleo::KeyRequester::~KeyRequester()
111 {
112 }
113 
114 const std::vector<GpgME::Key> &Kleo::KeyRequester::keys() const
115 {
116  return mKeys;
117 }
118 
119 const 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 
129 void 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 
140 void 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 
149 QString Kleo::KeyRequester::fingerprint() const
150 {
151  if (mKeys.empty()) {
152  return QString();
153  } else {
154  return QLatin1StringView(mKeys.front().primaryFingerprint());
155  }
156 }
157 
158 QStringList 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 
181 void 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__
230 static 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 
243 void 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) {
269  KMessageBox::error(this,
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) {
291  KMessageBox::error(this,
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 
316 void Kleo::KeyRequester::slotNextKey(const GpgME::Key &key)
317 {
318  if (!key.isNull()) {
319  mTmpKeys.push_back(key);
320  }
321 }
322 
323 void 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 
338 void 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 
355 void Kleo::KeyRequester::slotEraseButtonClicked()
356 {
357  if (!mKeys.empty()) {
358  Q_EMIT changed();
359  }
360  mKeys.clear();
361  updateKeys();
362 }
363 
364 void Kleo::KeyRequester::setDialogCaption(const QString &caption)
365 {
366  mDialogCaption = caption;
367 }
368 
369 void Kleo::KeyRequester::setDialogMessage(const QString &msg)
370 {
371  mDialogMessage = msg;
372 }
373 
374 bool Kleo::KeyRequester::isMultipleKeysEnabled() const
375 {
376  return mMulti;
377 }
378 
379 void 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 
393 unsigned int Kleo::KeyRequester::allowedKeys() const
394 {
395  return mKeyUsage;
396 }
397 
398 void 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 
423 QPushButton *Kleo::KeyRequester::dialogButton()
424 {
425  return mDialogButton;
426 }
427 
428 QPushButton *Kleo::KeyRequester::eraseButton()
429 {
430  return mEraseButton;
431 }
432 
433 static 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 
451 static 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 
456 static 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 
461 Kleo::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 
467 Kleo::EncryptionKeyRequester::EncryptionKeyRequester(QWidget *parent)
468  : KeyRequester(0, false, parent)
469  , d(nullptr)
470 {
471 }
472 
473 Kleo::EncryptionKeyRequester::~EncryptionKeyRequester()
474 {
475 }
476 
477 void Kleo::EncryptionKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
478 {
479  KeyRequester::setAllowedKeys(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
480 }
481 
482 Kleo::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 
488 Kleo::SigningKeyRequester::SigningKeyRequester(QWidget *parent)
489  : KeyRequester(0, false, parent)
490  , d(nullptr)
491 {
492 }
493 
494 Kleo::SigningKeyRequester::~SigningKeyRequester()
495 {
496 }
497 
498 void Kleo::SigningKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
499 {
500  KeyRequester::setAllowedKeys(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
501 }
502 
503 void Kleo::KeyRequester::virtual_hook(int, void *)
504 {
505 }
506 void Kleo::EncryptionKeyRequester::virtual_hook(int id, void *data)
507 {
508  KeyRequester::virtual_hook(id, data);
509 }
510 void Kleo::SigningKeyRequester::virtual_hook(int id, void *data)
511 {
512  KeyRequester::virtual_hook(id, data);
513 }
514 
515 #include "moc_keyrequester.cpp"
Base class for SigningKeyRequester and EncryptionKeyRequester.
Definition: keyrequester.h:41
DN parser and reorderer.
Definition: dn.h:26
QString fromUtf8(const char *str, int size)
void clicked(bool checked)
QIcon fromTheme(const QString &name)
void push_back(const T &value)
QString xi18n(const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void setFingerprint(const QString &fingerprint)
Set the key by fingerprint.
QString join(const QString &separator) const const
bool isRightToLeft()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString prettyDN() const
Definition: dn.cpp:439
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QString right(int n) const const
QList::iterator begin()
void setKey(const GpgME::Key &key)
Preferred method to set a key for non-multi-KeyRequesters.
QList::iterator end()
void setPixmap(const QPixmap &)
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.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:56:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.