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(i18nc("@action:button", "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 const QString keyID = QString::fromLatin1(it->keyID());
203 labelTexts.push_back(keyID);
204 toolTipText += keyID + QLatin1StringView(": ");
205 if (const char *uid = it->userID(0).id()) {
206 if (it->protocol() == GpgME::OpenPGP) {
207 toolTipText += QString::fromUtf8(uid);
208 } else {
209 toolTipText += Kleo::DN(uid).prettyDN();
210 }
211 } else {
212 toolTipText += xi18n("<placeholder>unknown</placeholder>");
213 }
214 toolTipText += QLatin1Char('\n');
215 }
216 if (mComplianceIcon) {
217 if (Kleo::all_of(mKeys, &Kleo::DeVSCompliance::keyIsCompliant)) {
218 mComplianceIcon->setPixmap(Formatting::successIcon().pixmap(22));
219 mComplianceIcon->setToolTip(DeVSCompliance::name(true));
220 } else {
221 mComplianceIcon->setPixmap(Formatting::warningIcon().pixmap(22));
222 mComplianceIcon->setToolTip(DeVSCompliance::name(false));
223 }
224 }
225 mLabel->setText(labelTexts.join(QLatin1StringView(", ")));
226 mLabel->setToolTip(toolTipText);
227}
228
229#ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
230#define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
231static void showKeyListError(QWidget *parent, const GpgME::Error &err)
232{
233 Q_ASSERT(err);
234 const QString msg = i18n(
235 "<qt><p>An error occurred while fetching "
236 "the keys from the backend:</p>"
237 "<p><b>%1</b></p></qt>",
238 Formatting::errorAsString(err));
239
240 KMessageBox::error(parent, msg, i18nc("@title:window", "Key Listing Failed"));
241}
242#endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
243
244void Kleo::KeyRequester::startKeyListJob(const QStringList &fingerprints)
245{
246 if (!mSMIMEBackend && !mOpenPGPBackend) {
247 return;
248 }
249
250 mTmpKeys.clear();
251 mJobs = 0;
252
253 unsigned int count = 0;
254 for (QStringList::const_iterator it = fingerprints.begin(); it != fingerprints.end(); ++it) {
255 if (!(*it).trimmed().isEmpty()) {
256 ++count;
257 }
258 }
259
260 if (!count) {
261 // don't fall into the trap that an empty pattern means
262 // "return all keys" :)
263 setKey(GpgME::Key::null);
264 return;
265 }
266
267 if (mOpenPGPBackend) {
268 KeyListJob *job = mOpenPGPBackend->keyListJob(false); // local, no sigs
269 if (!job) {
271 i18n("The OpenPGP backend does not support listing keys. "
272 "Check your installation."),
273 i18nc("@title:window", "Key Listing Failed"));
274 } else {
275 connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
276 connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
277
278 const GpgME::Error err =
279 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
280
281 if (err) {
282 showKeyListError(this, err);
283 } else {
284 ++mJobs;
285 }
286 }
287 }
288
289 if (mSMIMEBackend) {
290 KeyListJob *job = mSMIMEBackend->keyListJob(false); // local, no sigs
291 if (!job) {
293 i18n("The S/MIME backend does not support listing keys. "
294 "Check your installation."),
295 i18nc("@title:window", "Key Listing Failed"));
296 } else {
297 connect(job, &KeyListJob::result, this, &SigningKeyRequester::slotKeyListResult);
298 connect(job, &KeyListJob::nextKey, this, &SigningKeyRequester::slotNextKey);
299
300 const GpgME::Error err =
301 job->start(fingerprints, mKeyUsage & Kleo::KeySelectionDialog::SecretKeys && !(mKeyUsage & Kleo::KeySelectionDialog::PublicKeys));
302
303 if (err) {
304 showKeyListError(this, err);
305 } else {
306 ++mJobs;
307 }
308 }
309 }
310
311 if (mJobs > 0) {
312 mEraseButton->setEnabled(false);
313 mDialogButton->setEnabled(false);
314 }
315}
316
317void Kleo::KeyRequester::slotNextKey(const GpgME::Key &key)
318{
319 if (!key.isNull()) {
320 mTmpKeys.push_back(key);
321 }
322}
323
324void Kleo::KeyRequester::slotKeyListResult(const GpgME::KeyListResult &res)
325{
326 if (res.error()) {
327 showKeyListError(this, res.error());
328 }
329
330 if (--mJobs <= 0) {
331 mEraseButton->setEnabled(true);
332 mDialogButton->setEnabled(true);
333
334 setKeys(mTmpKeys);
335 mTmpKeys.clear();
336 }
337}
338
339void Kleo::KeyRequester::slotDialogButtonClicked()
340{
341 KeySelectionDialog *dlg = mKeys.empty() ? new KeySelectionDialog(mDialogCaption, mDialogMessage, mInitialQuery, mKeyUsage, mMulti, false, this)
342 : new KeySelectionDialog(mDialogCaption, mDialogCaption, mKeys, mKeyUsage, mMulti, false, this);
343
344 if (dlg->exec() == QDialog::Accepted) {
345 if (mMulti) {
346 setKeys(dlg->selectedKeys());
347 } else {
348 setKey(dlg->selectedKey());
349 }
350 Q_EMIT changed();
351 }
352
353 delete dlg;
354}
355
356void Kleo::KeyRequester::slotEraseButtonClicked()
357{
358 if (!mKeys.empty()) {
359 Q_EMIT changed();
360 }
361 mKeys.clear();
362 updateKeys();
363}
364
365void Kleo::KeyRequester::setDialogCaption(const QString &caption)
366{
367 mDialogCaption = caption;
368}
369
370void Kleo::KeyRequester::setDialogMessage(const QString &msg)
371{
372 mDialogMessage = msg;
373}
374
375bool Kleo::KeyRequester::isMultipleKeysEnabled() const
376{
377 return mMulti;
378}
379
380void Kleo::KeyRequester::setMultipleKeysEnabled(bool multi)
381{
382 if (multi == mMulti) {
383 return;
384 }
385
386 if (!multi && !mKeys.empty()) {
387 mKeys.erase(mKeys.begin() + 1, mKeys.end());
388 }
389
390 mMulti = multi;
391 updateKeys();
392}
393
394unsigned int Kleo::KeyRequester::allowedKeys() const
395{
396 return mKeyUsage;
397}
398
399void Kleo::KeyRequester::setAllowedKeys(unsigned int keyUsage)
400{
401 mKeyUsage = keyUsage;
402 mOpenPGPBackend = nullptr;
403 mSMIMEBackend = nullptr;
404
405 if (mKeyUsage & KeySelectionDialog::OpenPGPKeys) {
406 mOpenPGPBackend = openpgp();
407 }
408 if (mKeyUsage & KeySelectionDialog::SMIMEKeys) {
409 mSMIMEBackend = smime();
410 }
411
412 if (mOpenPGPBackend && !mSMIMEBackend) {
413 mDialogCaption = i18n("OpenPGP Key Selection");
414 mDialogMessage = i18n("Please select an OpenPGP key to use.");
415 } else if (!mOpenPGPBackend && mSMIMEBackend) {
416 mDialogCaption = i18n("S/MIME Key Selection");
417 mDialogMessage = i18n("Please select an S/MIME key to use.");
418 } else {
419 mDialogCaption = i18n("Key Selection");
420 mDialogMessage = i18n("Please select an (OpenPGP or S/MIME) key to use.");
421 }
422}
423
424QPushButton *Kleo::KeyRequester::dialogButton()
425{
426 return mDialogButton;
427}
428
429QPushButton *Kleo::KeyRequester::eraseButton()
430{
431 return mEraseButton;
432}
433
434static inline unsigned int foo(bool openpgp, bool smime, bool trusted, bool valid)
435{
436 unsigned int result = 0;
437 if (openpgp) {
438 result |= Kleo::KeySelectionDialog::OpenPGPKeys;
439 }
440 if (smime) {
441 result |= Kleo::KeySelectionDialog::SMIMEKeys;
442 }
443 if (trusted) {
444 result |= Kleo::KeySelectionDialog::TrustedKeys;
445 }
446 if (valid) {
447 result |= Kleo::KeySelectionDialog::ValidKeys;
448 }
449 return result;
450}
451
452static inline unsigned int encryptionKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
453{
454 return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::EncryptionKeys | Kleo::KeySelectionDialog::PublicKeys;
455}
456
457static inline unsigned int signingKeyUsage(bool openpgp, bool smime, bool trusted, bool valid)
458{
459 return foo(openpgp, smime, trusted, valid) | Kleo::KeySelectionDialog::SigningKeys | Kleo::KeySelectionDialog::SecretKeys;
460}
461
462Kleo::EncryptionKeyRequester::EncryptionKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
463 : KeyRequester(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
464 , d(nullptr)
465{
466}
467
468Kleo::EncryptionKeyRequester::EncryptionKeyRequester(QWidget *parent)
469 : KeyRequester(0, false, parent)
470 , d(nullptr)
471{
472}
473
474Kleo::EncryptionKeyRequester::~EncryptionKeyRequester()
475{
476}
477
478void Kleo::EncryptionKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
479{
480 KeyRequester::setAllowedKeys(encryptionKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
481}
482
483Kleo::SigningKeyRequester::SigningKeyRequester(bool multi, unsigned int proto, QWidget *parent, bool onlyTrusted, bool onlyValid)
484 : KeyRequester(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid), multi, parent)
485 , d(nullptr)
486{
487}
488
489Kleo::SigningKeyRequester::SigningKeyRequester(QWidget *parent)
490 : KeyRequester(0, false, parent)
491 , d(nullptr)
492{
493}
494
495Kleo::SigningKeyRequester::~SigningKeyRequester()
496{
497}
498
499void Kleo::SigningKeyRequester::setAllowedKeys(unsigned int proto, bool onlyTrusted, bool onlyValid)
500{
501 KeyRequester::setAllowedKeys(signingKeyUsage(proto & OpenPGP, proto & SMIME, onlyTrusted, onlyValid));
502}
503
504void Kleo::KeyRequester::virtual_hook(int, void *)
505{
506}
507void Kleo::EncryptionKeyRequester::virtual_hook(int id, void *data)
508{
509 KeyRequester::virtual_hook(id, data);
510}
511void Kleo::SigningKeyRequester::virtual_hook(int id, void *data)
512{
513 KeyRequester::virtual_hook(id, data);
514}
515
516#include "moc_keyrequester.cpp"
DN parser and reorderer.
Definition dn.h:27
QString prettyDN() const
Definition dn.cpp:445
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 error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QCA_EXPORT void init()
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 fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
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 Sat Dec 21 2024 16:56:14 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.