Libkleo

keyresolver.cpp
1/* -*- c++ -*-
2 keyresolver.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6 SPDX-FileCopyrightText: 2018 Intevation GmbH
7 SPDX-FileCopyrightText: 2021 g10 Code GmbH
8 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
9
10 Based on kpgp.cpp
11 SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
12 See file libkdenetwork/AUTHORS.kpgp for details
13
14 SPDX-License-Identifier: GPL-2.0-or-later
15*/
16
17#include <config-libkleo.h>
18
19#include "keyresolver.h"
20
21#include "keyresolvercore.h"
22
23#include <libkleo/formatting.h>
24#include <libkleo/keycache.h>
25#include <libkleo/keygroup.h>
26#include <libkleo/newkeyapprovaldialog.h>
27
28#include <libkleo_debug.h>
29
30#include <gpgme++/key.h>
31
32using namespace Kleo;
33using namespace GpgME;
34
35class KeyResolver::Private
36{
37public:
38 Private(KeyResolver *qq, bool enc, bool sig, Protocol fmt, bool allowMixed)
39 : q(qq)
40 , mCore(enc, sig, fmt)
41 , mFormat(fmt)
42 , mEncrypt(enc)
43 , mSign(sig)
44 , mAllowMixed(allowMixed)
45 , mCache(KeyCache::instance())
46 , mDialogWindowFlags(Qt::WindowFlags())
47 , mPreferredProtocol(UnknownProtocol)
48 {
49 mCore.setAllowMixedProtocols(allowMixed);
50 }
51
52 ~Private() = default;
53
54 KeyResolver::Solution expandUnresolvedGroups(KeyResolver::Solution solution);
55 void showApprovalDialog(KeyResolverCore::Result result, QWidget *parent);
56 void dialogAccepted();
57
58 KeyResolver *const q;
59 KeyResolverCore mCore;
60 Solution mResult;
61
62 Protocol mFormat;
63 bool mEncrypt;
64 bool mSign;
65 bool mAllowMixed;
66 // The cache is needed as a member variable to avoid rebuilding
67 // it between calls if we are the only user.
68 std::shared_ptr<const KeyCache> mCache;
69 std::unique_ptr<NewKeyApprovalDialog> mDialog;
70 Qt::WindowFlags mDialogWindowFlags;
71 Protocol mPreferredProtocol;
72};
73
74static bool lessThan(const Key &leftKey, const Key &rightKey)
75{
76 // shouldn't happen, but still put null keys at the end
77 if (leftKey.isNull()) {
78 return false;
79 }
80 if (rightKey.isNull()) {
81 return true;
82 }
83
84 // first sort by the displayed name and/or email address
85 const auto leftNameAndOrEmail = Formatting::nameAndEmailForSummaryLine(leftKey);
86 const auto rightNameAndOrEmail = Formatting::nameAndEmailForSummaryLine(rightKey);
87 const int cmp = QString::localeAwareCompare(leftNameAndOrEmail, rightNameAndOrEmail);
88 if (cmp) {
89 return cmp < 0;
90 }
91
92 // sort certificates with identical name/email address by their fingerprints
93 return strcmp(leftKey.primaryFingerprint(), rightKey.primaryFingerprint()) < 0;
94}
95
96KeyResolver::Solution KeyResolver::Private::expandUnresolvedGroups(KeyResolver::Solution solution)
97{
98 for (auto it = solution.encryptionKeys.begin(); it != solution.encryptionKeys.end(); ++it) {
99 const auto &address = it.key();
100 if (!it.value().empty()) {
101 continue;
102 }
103 const auto keyMatchingAddress = mCache->findBestByMailBox(address.toUtf8().constData(), solution.protocol, KeyCache::KeyUsage::Encrypt);
104 if (!keyMatchingAddress.isNull()) {
105 continue;
106 }
107 const auto groupMatchingAddress = mCache->findGroup(address, solution.protocol, KeyCache::KeyUsage::Encrypt);
108 if (!groupMatchingAddress.isNull()) {
109 qCDebug(LIBKLEO_LOG) << __func__ << "Expanding unresolved" << address << "with matching group";
110 const auto &groupKeys = groupMatchingAddress.keys();
111 std::vector<Key> keys;
112 keys.reserve(groupKeys.size());
113 std::copy(groupKeys.begin(), groupKeys.end(), std::back_inserter(keys));
114 std::sort(keys.begin(), keys.end(), lessThan);
115 it.value() = keys;
116 }
117 }
118
119 return solution;
120}
121
122void KeyResolver::Private::showApprovalDialog(KeyResolverCore::Result result, QWidget *parent)
123{
124 const auto preferredSolution = expandUnresolvedGroups(std::move(result.solution));
125 const auto alternativeSolution = expandUnresolvedGroups(std::move(result.alternative));
126
127 const QString sender = mCore.normalizedSender();
128 mDialog = std::make_unique<NewKeyApprovalDialog>(mEncrypt,
129 mSign,
130 sender,
131 std::move(preferredSolution),
132 std::move(alternativeSolution),
133 mAllowMixed,
134 mFormat,
135 parent,
136 mDialogWindowFlags);
137 connect(mDialog.get(), &QDialog::accepted, q, [this]() {
138 dialogAccepted();
139 });
140 connect(mDialog.get(), &QDialog::rejected, q, [this]() {
141 Q_EMIT q->keysResolved(false, false);
142 });
143 mDialog->open();
144}
145
146void KeyResolver::Private::dialogAccepted()
147{
148 mResult = mDialog->result();
149 Q_EMIT q->keysResolved(true, false);
150}
151
152void KeyResolver::start(bool showApproval, QWidget *parentWidget)
153{
154 qCDebug(LIBKLEO_LOG) << "Starting ";
155 if (!d->mSign && !d->mEncrypt) {
156 // nothing to do
157 return Q_EMIT keysResolved(true, true);
158 }
159 const auto result = d->mCore.resolve();
160 const bool success = (result.flags & KeyResolverCore::AllResolved);
161 if (success && !showApproval) {
162 d->mResult = std::move(result.solution);
163 Q_EMIT keysResolved(true, false);
164 return;
165 } else if (success) {
166 qCDebug(LIBKLEO_LOG) << "No need for the user showing approval anyway.";
167 }
168
169 d->showApprovalDialog(std::move(result), parentWidget);
170}
171
172KeyResolver::KeyResolver(bool encrypt, bool sign, Protocol fmt, bool allowMixed)
173 : d(new Private(this, encrypt, sign, fmt, allowMixed))
174{
175}
176
177Kleo::KeyResolver::~KeyResolver() = default;
178
179void KeyResolver::setRecipients(const QStringList &addresses)
180{
181 d->mCore.setRecipients(addresses);
182}
183
184void KeyResolver::setSender(const QString &address)
185{
186 d->mCore.setSender(address);
187}
188
189void KeyResolver::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
190{
191 d->mCore.setOverrideKeys(overrides);
192}
193
194void KeyResolver::setSigningKeys(const QStringList &fingerprints)
195{
196 d->mCore.setSigningKeys(fingerprints);
197}
198
199KeyResolver::Solution KeyResolver::result() const
200{
201 return d->mResult;
202}
203
204void KeyResolver::setDialogWindowFlags(Qt::WindowFlags flags)
205{
206 d->mDialogWindowFlags = flags;
207}
208
209void KeyResolver::setPreferredProtocol(Protocol proto)
210{
211 d->mCore.setPreferredProtocol(proto);
212}
213
214void KeyResolver::setMinimumValidity(int validity)
215{
216 d->mCore.setMinimumValidity(validity);
217}
218
219#include "moc_keyresolver.cpp"
Kleo::Result setSigningKeys(const QStringList &fingerprints)
PostalAddress address(const QVariant &location)
void accepted()
void rejected()
QMap::iterator begin()
QMap::iterator end()
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QObject * sender() const const
UnknownProtocol
int localeAwareCompare(const QString &other) const const
typedef WindowFlags
Solution represents the solution found by the KeyResolver.
Definition keyresolver.h:65
GpgME::Protocol protocol
This property holds a hint at the protocol of the signing and encryption keys, i.e.
Definition keyresolver.h:72
QMap< QString, std::vector< GpgME::Key > > encryptionKeys
This property contains the encryption keys to use for the different recipients.
Definition keyresolver.h:91
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:39:42 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.