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(i18nc("All Certificates", "All"));
72 setDescription(i18n("All certificates"));
73 setId(QStringLiteral("all-certificates"));
74 setMatchContexts(Filtering);
75 }
76};
77
78class MyCertificatesKeyFilter : public DefaultKeyFilter
79{
80public:
81 MyCertificatesKeyFilter()
83 {
84 setHasSecret(Set);
85 setSpecificity(UINT_MAX - 1); // overly high for ordering
86
87 setName(i18nc("My own Certificates", "My Own"));
88 setDescription(i18n("My own certificates"));
89 setId(QStringLiteral("my-certificates"));
90 setMatchContexts(AnyMatchContext);
91 setBold(true);
92 }
93};
94
95class FullCertificatesKeyFilter : public DefaultKeyFilter
96{
97public:
98 FullCertificatesKeyFilter()
100 {
101 setRevoked(NotSet);
102 setValidity(IsAtLeast);
103 setValidityReferenceLevel(UserID::Full);
104 setSpecificity(UINT_MAX - 3);
105
106 setName(i18nc("Certified Certificates", "Certified"));
107 setDescription(i18n("Certificates for which the primary user ID is certified"));
108 setId(QStringLiteral("trusted-certificates"));
109 setMatchContexts(Filtering);
110 }
111};
112
113class OtherCertificatesKeyFilter : public DefaultKeyFilter
114{
115public:
116 OtherCertificatesKeyFilter()
118 {
119 setHasSecret(NotSet);
120 setValidity(IsAtMost);
121 setValidityReferenceLevel(UserID::Marginal);
122 setSpecificity(UINT_MAX - 5); // overly high for ordering
123
124 setName(i18nc("Not Certified Certificates", "Not Certified"));
125 setDescription(i18n("Certificates for which the primary user ID is not certified"));
126 setId(QStringLiteral("other-certificates"));
127 setMatchContexts(Filtering);
128 }
129};
130
131/* This filter selects uncertified OpenPGP keys, i.e. "good" OpenPGP keys with
132 * unrevoked user IDs that are not fully valid. */
133class UncertifiedOpenPGPKeysFilter : public DefaultKeyFilter
134{
135public:
136 UncertifiedOpenPGPKeysFilter()
138 {
139 setSpecificity(UINT_MAX - 6); // overly high for ordering
140 setName(i18nc("Certificates to certify by the user", "To Certify"));
141 setDescription(i18n("Certificates that are not fully certified and that you may want to certify yourself"));
142 setId(QStringLiteral("not-certified-certificates"));
143
144 setMatchContexts(Filtering);
145 setIsOpenPGP(Set);
146 setIsBad(NotSet);
147 }
148 bool matches(const Key &key, MatchContexts contexts) const override
149 {
150 return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
151 }
152 bool matches(const UserID &userID, MatchContexts contexts) const override
153 {
154 return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
155 }
156};
157
158/* This filter selects only invalid keys (i.e. those where not all
159 * UIDs are at least fully valid). */
160class KeyNotValidFilter : public DefaultKeyFilter
161{
162public:
163 KeyNotValidFilter()
165 {
166 setSpecificity(UINT_MAX - 4); // overly high for ordering
167
168 setName(i18nc("Not Fully Certified Certificates", "Not Fully Certified"));
169 setDescription(i18n("Certificates for which not all user IDs are certified"));
170 setId(QStringLiteral("not-validated-certificates"));
171 setMatchContexts(Filtering);
172 }
173 bool matches(const Key &key, MatchContexts contexts) const override
174 {
175 return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
176 }
177 bool matches(const UserID &userID, MatchContexts contexts) const override
178 {
179 return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
180 }
181};
182
183}
184
185class KeyFullyCertifiedFilter : public DefaultKeyFilter
186{
187public:
188 KeyFullyCertifiedFilter()
190 {
191 setSpecificity(UINT_MAX - 2);
192 setName(i18nc("Fully Certified Certificates", "Fully Certified"));
193 setDescription(i18n("Certificates for which all user IDs are certified"));
194 setId(QStringLiteral("full-certificates"));
195 setMatchContexts(Filtering);
196 }
197 bool matches(const Key &key, MatchContexts contexts) const override
198 {
199 return DefaultKeyFilter::matches(key, contexts) && Kleo::allUserIDsHaveFullValidity(key);
200 }
201 bool matches(const UserID &userID, MatchContexts contexts) const override
202 {
203 return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() >= UserID::Full;
204 }
205};
206
207static std::vector<std::shared_ptr<KeyFilter>> defaultFilters()
208{
209 return {
210 std::shared_ptr<KeyFilter>(new MyCertificatesKeyFilter),
211 std::shared_ptr<KeyFilter>(new FullCertificatesKeyFilter),
212 std::shared_ptr<KeyFilter>(new OtherCertificatesKeyFilter),
213 std::shared_ptr<KeyFilter>(new AllCertificatesKeyFilter),
214 std::shared_ptr<KeyFilter>(new UncertifiedOpenPGPKeysFilter),
215 std::shared_ptr<KeyFilter>(new KeyFullyCertifiedFilter),
216 std::shared_ptr<KeyFilter>(new KeyNotValidFilter),
217 };
218}
219
220class KeyFilterManager::Private
221{
222public:
223 Private()
224 : filters()
225 , model(this)
226 {
227 }
228 void clear()
229 {
230 model.beginResetModel();
231 filters.clear();
232 model.endResetModel();
233 }
234
235 std::vector<std::shared_ptr<KeyFilter>> filters;
236 Model model;
237 GpgME::Protocol protocol = GpgME::UnknownProtocol;
238};
239
240KeyFilterManager *KeyFilterManager::mSelf = nullptr;
241
242KeyFilterManager::KeyFilterManager(QObject *parent)
243 : QObject(parent)
244 , d(new Private)
245{
246 mSelf = this;
247 // ### DF: doesn't a KStaticDeleter work more reliably?
250 }
251 reload();
252}
253
254KeyFilterManager::~KeyFilterManager()
255{
256 mSelf = nullptr;
257 if (d) {
258 d->clear();
259 }
260}
261
262KeyFilterManager *KeyFilterManager::instance()
263{
264 if (!mSelf) {
265 mSelf = new KeyFilterManager();
266 }
267 return mSelf;
268}
269
270void KeyFilterManager::alwaysFilterByProtocol(GpgME::Protocol protocol)
271{
272 if (protocol != d->protocol) {
273 d->protocol = protocol;
274 reload();
275 }
276}
277
278const std::shared_ptr<KeyFilter> &KeyFilterManager::filterMatching(const Key &key, KeyFilter::MatchContexts contexts) const
279{
280 const auto it = std::find_if(d->filters.cbegin(), d->filters.cend(), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
281 return filter->matches(key, contexts);
282 });
283 if (it != d->filters.cend()) {
284 return *it;
285 }
286 static const std::shared_ptr<KeyFilter> null;
287 return null;
288}
289
290std::vector<std::shared_ptr<KeyFilter>> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const
291{
292 std::vector<std::shared_ptr<KeyFilter>> result;
293 result.reserve(d->filters.size());
294 std::remove_copy_if(d->filters.begin(), d->filters.end(), std::back_inserter(result), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
295 return !filter->matches(key, contexts);
296 });
297 return result;
298}
299
300namespace
301{
302static const auto byDecreasingSpecificity = [](const std::shared_ptr<KeyFilter> &lhs, const std::shared_ptr<KeyFilter> &rhs) {
303 return lhs->specificity() > rhs->specificity();
304};
305}
306
307void KeyFilterManager::reload()
308{
309 d->clear();
310
311 d->filters = defaultFilters();
312 KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
313
314 const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
315 const bool ignoreDeVs = !DeVSCompliance::isCompliant();
316 for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) {
317 const KConfigGroup cfg(config, *it);
318 if (cfg.hasKey("is-de-vs") && ignoreDeVs) {
319 /* Don't show de-vs filters in other compliance modes */
320 continue;
321 }
322 d->filters.push_back(std::shared_ptr<KeyFilter>(new KConfigBasedKeyFilter(cfg)));
323 }
324 std::stable_sort(d->filters.begin(), d->filters.end(), byDecreasingSpecificity);
325
326 if (d->protocol != GpgME::UnknownProtocol) {
327 // remove filters with conflicting isOpenPGP rule
328 const auto conflictingValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
329 Kleo::erase_if(d->filters, [conflictingValue](const auto &f) {
330 const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
331 Q_ASSERT(filter);
332 return filter->isOpenPGP() == conflictingValue;
333 });
334 // add isOpenPGP rule to all filters
335 const auto isOpenPGPValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
336 std::for_each(std::begin(d->filters), std::end(d->filters), [isOpenPGPValue](auto &f) {
337 const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
338 Q_ASSERT(filter);
339 return filter->setIsOpenPGP(isOpenPGPValue);
340 });
341 }
342 qCDebug(LIBKLEO_LOG) << "KeyFilterManager::" << __func__ << "final filter count is" << d->filters.size();
343}
344
345QAbstractItemModel *KeyFilterManager::model() const
346{
347 return &d->model;
348}
349
350const std::shared_ptr<KeyFilter> &KeyFilterManager::keyFilterByID(const QString &id) const
351{
352 const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr<KeyFilter> &filter) {
353 return filter->id() == id;
354 });
355 if (it != d->filters.end()) {
356 return *it;
357 }
358 static const std::shared_ptr<KeyFilter> null;
359 return null;
360}
361
362const std::shared_ptr<KeyFilter> &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const
363{
364 if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast<unsigned>(idx.row()) >= d->filters.size()) {
365 static const std::shared_ptr<KeyFilter> null;
366 return null;
367 }
368 return d->filters[idx.row()];
369}
370
371QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr<KeyFilter> &kf) const
372{
373 if (!kf) {
374 return {};
375 }
376 const auto pair = std::equal_range(d->filters.cbegin(), d->filters.cend(), kf, byDecreasingSpecificity);
377 const auto it = std::find(pair.first, pair.second, kf);
378 if (it != pair.second) {
379 return d->model.index(it - d->filters.begin());
380 } else {
381 return QModelIndex();
382 }
383}
384
385int Model::rowCount(const QModelIndex &) const
386{
387 return m_keyFilterManagerPrivate->filters.size();
388}
389
390QVariant Model::data(const QModelIndex &idx, int role) const
391{
392 if (!idx.isValid() || idx.model() != this || idx.row() < 0 || static_cast<unsigned>(idx.row()) > m_keyFilterManagerPrivate->filters.size()) {
393 return QVariant();
394 }
395
396 const auto filter = m_keyFilterManagerPrivate->filters[idx.row()];
397 switch (role) {
399 return filter->icon();
400
401 case Qt::DisplayRole:
402 case Qt::EditRole:
403 return filter->name();
404 case Qt::ToolTipRole:
405 return filter->description();
406
407 case KeyFilterManager::FilterIdRole:
408 return filter->id();
409
410 case KeyFilterManager::FilterMatchContextsRole:
411 return QVariant::fromValue(filter->availableMatchContexts());
412
413 default:
414 return QVariant();
415 }
416}
417
418static KeyFilter::FontDescription
419get_fontdescription(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, const KeyFilter::FontDescription &initial)
420{
421 return kdtools::accumulate_if(
422 filters.begin(),
423 filters.end(),
424 [&key](const std::shared_ptr<KeyFilter> &filter) {
425 return filter->matches(key, KeyFilter::Appearance);
426 },
427 initial,
428 [](const KeyFilter::FontDescription &lhs, const std::shared_ptr<KeyFilter> &rhs) {
429 return lhs.resolve(rhs->fontDescription());
430 });
431}
432
433QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const
434{
435 const KeyFilter::FontDescription fd = get_fontdescription(d->filters, key, KeyFilter::FontDescription());
436
437 return fd.font(baseFont);
438}
439
440static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QColor (KeyFilter::*fun)() const)
441{
442 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
443 return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
444 });
445 if (it == filters.cend()) {
446 return {};
447 } else {
448 return (it->get()->*fun)();
449 }
450}
451
452static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const UserID &userID, QColor (KeyFilter::*fun)() const)
453{
454 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &userID](const std::shared_ptr<KeyFilter> &filter) {
455 return filter->matches(userID, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
456 });
457 if (it == filters.cend()) {
458 return {};
459 } else {
460 return (it->get()->*fun)();
461 }
462}
463
464static QString get_string(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QString (KeyFilter::*fun)() const)
465{
466 const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
467 return filter->matches(key, KeyFilter::Appearance) && !(filter.get()->*fun)().isEmpty();
468 });
469 if (it == filters.cend()) {
470 return QString();
471 } else {
472 return (*it)->icon();
473 }
474}
475
476QColor KeyFilterManager::bgColor(const Key &key) const
477{
478 return get_color(d->filters, key, &KeyFilter::bgColor);
479}
480
481QColor KeyFilterManager::fgColor(const Key &key) const
482{
483 return get_color(d->filters, key, &KeyFilter::fgColor);
484}
485
486QColor KeyFilterManager::bgColor(const UserID &userID) const
487{
488 return get_color(d->filters, userID, &KeyFilter::bgColor);
489}
490
491QColor KeyFilterManager::fgColor(const UserID &userID) const
492{
493 return get_color(d->filters, userID, &KeyFilter::fgColor);
494}
495
496QIcon KeyFilterManager::icon(const Key &key) const
497{
498 const QString icon = get_string(d->filters, key, &KeyFilter::icon);
499 return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon);
500}
501
502#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 i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & reload()
QCoreApplication * instance()
iterator begin()
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(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:50:31 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.