Libkleo

keyfiltermanager.cpp
1/*
2 keyfiltermanager.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include <config-libkleo.h>
11
12#include "keyfiltermanager.h"
13
14#include "defaultkeyfilter.h"
15#include "kconfigbasedkeyfilter.h"
16#include "stl_util.h"
17
18#include <libkleo/algorithm.h>
19#include <libkleo/compliance.h>
20#include <libkleo/gnupg.h>
21#include <libkleo/keyhelpers.h>
22
23#include <libkleo_debug.h>
24
25#include <KConfig>
26#include <KConfigGroup>
27#include <KLocalizedString>
28#include <KSharedConfig>
29
30#include <QAbstractListModel>
31#include <QCoreApplication>
32#include <QIcon>
33#include <QModelIndex>
34#include <QRegularExpression>
35#include <QStringList>
36
37#include <algorithm>
38#include <climits>
39#include <functional>
40
41using namespace Kleo;
42using namespace GpgME;
43
44namespace
45{
46
47class Model : public QAbstractListModel
48{
49 KeyFilterManager::Private *m_keyFilterManagerPrivate;
50
51public:
52 explicit Model(KeyFilterManager::Private *p)
53 : QAbstractListModel(nullptr)
54 , m_keyFilterManagerPrivate(p)
55 {
56 }
57
58 int rowCount(const QModelIndex &) const override;
59 QVariant data(const QModelIndex &idx, int role) const override;
60 /* upgrade to public */ using QAbstractListModel::beginResetModel;
61 /* upgrade to public */ using QAbstractListModel::endResetModel;
62};
63
64class AllCertificatesKeyFilter : public DefaultKeyFilter
65{
66public:
67 AllCertificatesKeyFilter()
69 {
70 setSpecificity(UINT_MAX); // overly high for ordering
71 setName(i18n("All Certificates"));
72 setId(QStringLiteral("all-certificates"));
73 setMatchContexts(Filtering);
74 }
75};
76
77class MyCertificatesKeyFilter : public DefaultKeyFilter
78{
79public:
80 MyCertificatesKeyFilter()
82 {
83 setHasSecret(Set);
84 setSpecificity(UINT_MAX - 1); // overly high for ordering
85
86 setName(i18n("My Certificates"));
87 setId(QStringLiteral("my-certificates"));
88 setMatchContexts(AnyMatchContext);
89 setBold(true);
90 }
91};
92
93class TrustedCertificatesKeyFilter : public DefaultKeyFilter
94{
95public:
96 TrustedCertificatesKeyFilter()
98 {
99 setRevoked(NotSet);
100 setValidity(IsAtLeast);
101 setValidityReferenceLevel(UserID::Marginal);
102 setSpecificity(UINT_MAX - 2); // overly high for ordering
103
104 setName(i18n("Trusted Certificates"));
105 setId(QStringLiteral("trusted-certificates"));
106 setMatchContexts(Filtering);
107 }
108};
109
110class FullCertificatesKeyFilter : public DefaultKeyFilter
111{
112public:
113 FullCertificatesKeyFilter()
115 {
116 setRevoked(NotSet);
117 setValidity(IsAtLeast);
118 setValidityReferenceLevel(UserID::Full);
119 setSpecificity(UINT_MAX - 3);
120
121 setName(i18n("Fully Trusted Certificates"));
122 setId(QStringLiteral("full-certificates"));
123 setMatchContexts(Filtering);
124 }
125};
126
127class OtherCertificatesKeyFilter : public DefaultKeyFilter
128{
129public:
130 OtherCertificatesKeyFilter()
132 {
133 setHasSecret(NotSet);
134 setValidity(IsAtMost);
135 setValidityReferenceLevel(UserID::Never);
136 setSpecificity(UINT_MAX - 4); // overly high for ordering
137
138 setName(i18n("Other Certificates"));
139 setId(QStringLiteral("other-certificates"));
140 setMatchContexts(Filtering);
141 }
142};
143
144/* This filter selects uncertified OpenPGP keys, i.e. "good" OpenPGP keys with
145 * unrevoked user IDs that are not fully valid. */
146class UncertifiedOpenPGPKeysFilter : public DefaultKeyFilter
147{
148public:
149 UncertifiedOpenPGPKeysFilter()
151 {
152 setSpecificity(UINT_MAX - 6); // overly high for ordering
153 setName(i18n("Not Certified Certificates"));
154 setId(QStringLiteral("not-certified-certificates"));
155
156 setMatchContexts(Filtering);
157 setIsOpenPGP(Set);
158 setIsBad(NotSet);
159 }
160 bool matches(const Key &key, MatchContexts contexts) const override
161 {
162 return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
163 }
164 bool matches(const UserID &userID, MatchContexts contexts) const override
165 {
166 return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
167 }
168};
169
170/* This filter selects only invalid keys (i.e. those where not all
171 * UIDs are at least fully valid). */
172class KeyNotValidFilter : public DefaultKeyFilter
173{
174public:
175 KeyNotValidFilter()
177 {
178 setSpecificity(UINT_MAX - 7); // overly high for ordering
179
180 setName(i18n("Not Validated Certificates"));
181 setId(QStringLiteral("not-validated-certificates"));
182 setMatchContexts(Filtering);
183 }
184 bool matches(const Key &key, MatchContexts contexts) const override
185 {
186 return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
187 }
188 bool matches(const UserID &userID, MatchContexts contexts) const override
189 {
190 return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
191 }
192};
193
194}
195
196static std::vector<std::shared_ptr<KeyFilter>> defaultFilters()
197{
198 std::vector<std::shared_ptr<KeyFilter>> result;
199 result.reserve(6);
200 result.push_back(std::shared_ptr<KeyFilter>(new MyCertificatesKeyFilter));
201 result.push_back(std::shared_ptr<KeyFilter>(new TrustedCertificatesKeyFilter));
202 result.push_back(std::shared_ptr<KeyFilter>(new FullCertificatesKeyFilter));
203 result.push_back(std::shared_ptr<KeyFilter>(new OtherCertificatesKeyFilter));
204 result.push_back(std::shared_ptr<KeyFilter>(new AllCertificatesKeyFilter));
205 result.push_back(std::shared_ptr<KeyFilter>(new UncertifiedOpenPGPKeysFilter));
206 result.push_back(std::shared_ptr<KeyFilter>(new KeyNotValidFilter));
207 return result;
208}
209
210class KeyFilterManager::Private
211{
212public:
213 Private()
214 : filters()
215 , model(this)
216 {
217 }
218 void clear()
219 {
220 model.beginResetModel();
221 filters.clear();
222 model.endResetModel();
223 }
224
225 std::vector<std::shared_ptr<KeyFilter>> filters;
226 Model model;
227 GpgME::Protocol protocol = GpgME::UnknownProtocol;
228};
229
230KeyFilterManager *KeyFilterManager::mSelf = nullptr;
231
232KeyFilterManager::KeyFilterManager(QObject *parent)
233 : QObject(parent)
234 , d(new Private)
235{
236 mSelf = this;
237 // ### DF: doesn't a KStaticDeleter work more reliably?
240 }
241 reload();
242}
243
244KeyFilterManager::~KeyFilterManager()
245{
246 mSelf = nullptr;
247 if (d) {
248 d->clear();
249 }
250}
251
252KeyFilterManager *KeyFilterManager::instance()
253{
254 if (!mSelf) {
255 mSelf = new KeyFilterManager();
256 }
257 return mSelf;
258}
259
260void KeyFilterManager::alwaysFilterByProtocol(GpgME::Protocol protocol)
261{
262 if (protocol != d->protocol) {
263 d->protocol = protocol;
264 reload();
265 }
266}
267
268const std::shared_ptr<KeyFilter> &KeyFilterManager::filterMatching(const Key &key, KeyFilter::MatchContexts contexts) const
269{
270 const auto it = std::find_if(d->filters.cbegin(), d->filters.cend(), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
271 return filter->matches(key, contexts);
272 });
273 if (it != d->filters.cend()) {
274 return *it;
275 }
276 static const std::shared_ptr<KeyFilter> null;
277 return null;
278}
279
280std::vector<std::shared_ptr<KeyFilter>> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const
281{
282 std::vector<std::shared_ptr<KeyFilter>> result;
283 result.reserve(d->filters.size());
284 std::remove_copy_if(d->filters.begin(), d->filters.end(), std::back_inserter(result), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
285 return !filter->matches(key, contexts);
286 });
287 return result;
288}
289
290namespace
291{
292static const auto byDecreasingSpecificity = [](const std::shared_ptr<KeyFilter> &lhs, const std::shared_ptr<KeyFilter> &rhs) {
293 return lhs->specificity() > rhs->specificity();
294};
295}
296
297void KeyFilterManager::reload()
298{
299 d->clear();
300
301 d->filters = defaultFilters();
302 KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
303
304 const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
305 const bool ignoreDeVs = !DeVSCompliance::isCompliant();
306 for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) {
307 const KConfigGroup cfg(config, *it);
308 if (cfg.hasKey("is-de-vs") && ignoreDeVs) {
309 /* Don't show de-vs filters in other compliance modes */
310 continue;
311 }
312 d->filters.push_back(std::shared_ptr<KeyFilter>(new KConfigBasedKeyFilter(cfg)));
313 }
314 std::stable_sort(d->filters.begin(), d->filters.end(), byDecreasingSpecificity);
315
316 if (d->protocol != GpgME::UnknownProtocol) {
317 // remove filters with conflicting isOpenPGP rule
318 const auto conflictingValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
319 Kleo::erase_if(d->filters, [conflictingValue](const auto &f) {
320 const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
321 Q_ASSERT(filter);
322 return filter->isOpenPGP() == conflictingValue;
323 });
324 // add isOpenPGP rule to all filters
325 const auto isOpenPGPValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
326 std::for_each(std::begin(d->filters), std::end(d->filters), [isOpenPGPValue](auto &f) {
327 const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
328 Q_ASSERT(filter);
329 return filter->setIsOpenPGP(isOpenPGPValue);
330 });
331 }
332 qCDebug(LIBKLEO_LOG) << "KeyFilterManager::" << __func__ << "final filter count is" << d->filters.size();
333}
334
335QAbstractItemModel *KeyFilterManager::model() const
336{
337 return &d->model;
338}
339
340const std::shared_ptr<KeyFilter> &KeyFilterManager::keyFilterByID(const QString &id) const
341{
342 const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr<KeyFilter> &filter) {
343 return filter->id() == id;
344 });
345 if (it != d->filters.end()) {
346 return *it;
347 }
348 static const std::shared_ptr<KeyFilter> null;
349 return null;
350}
351
352const std::shared_ptr<KeyFilter> &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const
353{
354 if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast<unsigned>(idx.row()) >= d->filters.size()) {
355 static const std::shared_ptr<KeyFilter> null;
356 return null;
357 }
358 return d->filters[idx.row()];
359}
360
361QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr<KeyFilter> &kf) const
362{
363 if (!kf) {
364 return {};
365 }
366 const auto pair = std::equal_range(d->filters.cbegin(), d->filters.cend(), kf, byDecreasingSpecificity);
367 const auto it = std::find(pair.first, pair.second, kf);
368 if (it != pair.second) {
369 return d->model.index(it - d->filters.begin());
370 } else {
371 return QModelIndex();
372 }
373}
374
375int Model::rowCount(const QModelIndex &) const
376{
377 return m_keyFilterManagerPrivate->filters.size();
378}
379
380QVariant Model::data(const QModelIndex &idx, int role) const
381{
382 if (!idx.isValid() || idx.model() != this || idx.row() < 0 || static_cast<unsigned>(idx.row()) > m_keyFilterManagerPrivate->filters.size()) {
383 return QVariant();
384 }
385
386 const auto filter = m_keyFilterManagerPrivate->filters[idx.row()];
387 switch (role) {
389 return filter->icon();
390
391 case Qt::DisplayRole:
392 case Qt::EditRole:
393 case Qt::ToolTipRole: /* Most useless tooltip ever. */
394 return filter->name();
395
396 case KeyFilterManager::FilterIdRole:
397 return filter->id();
398
399 case KeyFilterManager::FilterMatchContextsRole:
400 return QVariant::fromValue(filter->availableMatchContexts());
401
402 default:
403 return QVariant();
404 }
405}
406
407static KeyFilter::FontDescription
408get_fontdescription(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, const KeyFilter::FontDescription &initial)
409{
410 return kdtools::accumulate_if(
411 filters.begin(),
412 filters.end(),
413 [&key](const std::shared_ptr<KeyFilter> &filter) {
414 return filter->matches(key, KeyFilter::Appearance);
415 },
416 initial,
417 [](const KeyFilter::FontDescription &lhs, const std::shared_ptr<KeyFilter> &rhs) {
418 return lhs.resolve(rhs->fontDescription());
419 });
420}
421
422QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const
423{
424 const KeyFilter::FontDescription fd = get_fontdescription(d->filters, key, KeyFilter::FontDescription());
425
426 return fd.font(baseFont);
427}
428
429static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QColor (KeyFilter::*fun)() const)
430{
431 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
432 return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
433 });
434 if (it == filters.cend()) {
435 return {};
436 } else {
437 return (it->get()->*fun)();
438 }
439}
440
441static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const UserID &userID, QColor (KeyFilter::*fun)() const)
442{
443 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &userID](const std::shared_ptr<KeyFilter> &filter) {
444 return filter->matches(userID, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
445 });
446 if (it == filters.cend()) {
447 return {};
448 } else {
449 return (it->get()->*fun)();
450 }
451}
452
453static QString get_string(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QString (KeyFilter::*fun)() const)
454{
455 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
456 return filter->matches(key, KeyFilter::Appearance) && !(filter.get()->*fun)().isEmpty();
457 });
458 if (it == filters.cend()) {
459 return QString();
460 } else {
461 return (*it)->icon();
462 }
463}
464
465QColor KeyFilterManager::bgColor(const Key &key) const
466{
467 return get_color(d->filters, key, &KeyFilter::bgColor);
468}
469
470QColor KeyFilterManager::fgColor(const Key &key) const
471{
472 return get_color(d->filters, key, &KeyFilter::fgColor);
473}
474
475QColor KeyFilterManager::bgColor(const UserID &userID) const
476{
477 return get_color(d->filters, userID, &KeyFilter::bgColor);
478}
479
480QColor KeyFilterManager::fgColor(const UserID &userID) const
481{
482 return get_color(d->filters, userID, &KeyFilter::fgColor);
483}
484
485QIcon KeyFilterManager::icon(const Key &key) const
486{
487 const QString icon = get_string(d->filters, key, &KeyFilter::icon);
488 return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon);
489}
490
491#include "moc_keyfiltermanager.cpp"
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Default implementation of key filter class.
An abstract base class key filters.
Definition keyfilter.h:37
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & reload()
QCoreApplication * instance()
QList::iterator begin()
QList::iterator end()
bool isValid() const const
const QAbstractItemModel * model() const const
int row() const const
void deleteLater()
bool isEmpty() const const
QStringList filter(QStringView str, Qt::CaseSensitivity cs) const const
DecorationRole
QFuture< void > filter(Sequence &sequence, KeepFunctor filterFunction)
QVariant fromValue(const T &value)
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.