Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F19741706
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
22 KB
Subscribers
None
View Options
diff --git a/src/kleo/keyfiltermanager.cpp b/src/kleo/keyfiltermanager.cpp
index 9be56d99..d9f42bbd 100644
--- a/src/kleo/keyfiltermanager.cpp
+++ b/src/kleo/keyfiltermanager.cpp
@@ -1,481 +1,489 @@
/*
keyfiltermanager.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "keyfiltermanager.h"
#include "defaultkeyfilter.h"
#include "kconfigbasedkeyfilter.h"
#include "stl_util.h"
#include <libkleo/algorithm.h>
#include <libkleo/compliance.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyhelpers.h>
#include <libkleo_debug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QAbstractListModel>
#include <QCoreApplication>
#include <QIcon>
#include <QModelIndex>
#include <QRegularExpression>
#include <QStringList>
#include <algorithm>
#include <climits>
#include <functional>
using namespace Kleo;
using namespace GpgME;
namespace
{
class Model : public QAbstractListModel
{
KeyFilterManager::Private *m_keyFilterManagerPrivate;
public:
explicit Model(KeyFilterManager::Private *p)
: QAbstractListModel(nullptr)
, m_keyFilterManagerPrivate(p)
{
}
int rowCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &idx, int role) const override;
/* upgrade to public */ using QAbstractListModel::beginResetModel;
/* upgrade to public */ using QAbstractListModel::endResetModel;
};
class AllCertificatesKeyFilter : public DefaultKeyFilter
{
public:
AllCertificatesKeyFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX); // overly high for ordering
setName(i18n("All Certificates"));
setId(QStringLiteral("all-certificates"));
setMatchContexts(Filtering);
}
};
class MyCertificatesKeyFilter : public DefaultKeyFilter
{
public:
MyCertificatesKeyFilter()
: DefaultKeyFilter()
{
setHasSecret(Set);
setSpecificity(UINT_MAX - 1); // overly high for ordering
setName(i18n("My Certificates"));
setId(QStringLiteral("my-certificates"));
setMatchContexts(AnyMatchContext);
setBold(true);
}
};
class TrustedCertificatesKeyFilter : public DefaultKeyFilter
{
public:
TrustedCertificatesKeyFilter()
: DefaultKeyFilter()
{
setRevoked(NotSet);
setValidity(IsAtLeast);
setValidityReferenceLevel(UserID::Marginal);
setSpecificity(UINT_MAX - 2); // overly high for ordering
setName(i18n("Trusted Certificates"));
setId(QStringLiteral("trusted-certificates"));
setMatchContexts(Filtering);
}
};
class FullCertificatesKeyFilter : public DefaultKeyFilter
{
public:
FullCertificatesKeyFilter()
: DefaultKeyFilter()
{
setRevoked(NotSet);
setValidity(IsAtLeast);
setValidityReferenceLevel(UserID::Full);
setSpecificity(UINT_MAX - 3);
setName(i18n("Fully Trusted Certificates"));
setId(QStringLiteral("full-certificates"));
setMatchContexts(Filtering);
}
};
class OtherCertificatesKeyFilter : public DefaultKeyFilter
{
public:
OtherCertificatesKeyFilter()
: DefaultKeyFilter()
{
setHasSecret(NotSet);
setValidity(IsAtMost);
setValidityReferenceLevel(UserID::Never);
setSpecificity(UINT_MAX - 4); // overly high for ordering
setName(i18n("Other Certificates"));
setId(QStringLiteral("other-certificates"));
setMatchContexts(Filtering);
}
};
/* This filter selects uncertified OpenPGP keys, i.e. "good" OpenPGP keys with
* unrevoked user IDs that are not fully valid. */
class UncertifiedOpenPGPKeysFilter : public DefaultKeyFilter
{
public:
UncertifiedOpenPGPKeysFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX - 6); // overly high for ordering
setName(i18n("Not Certified Certificates"));
setId(QStringLiteral("not-certified-certificates"));
setMatchContexts(Filtering);
setIsOpenPGP(Set);
setIsBad(NotSet);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
+ bool matches(const UserID &userID, MatchContexts contexts) const override
+ {
+ return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
+ }
};
/* This filter selects only invalid keys (i.e. those where not all
* UIDs are at least fully valid). */
class KeyNotValidFilter : public DefaultKeyFilter
{
public:
KeyNotValidFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX - 7); // overly high for ordering
setName(i18n("Not Validated Certificates"));
setId(QStringLiteral("not-validated-certificates"));
setMatchContexts(Filtering);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
+ bool matches(const UserID &userID, MatchContexts contexts) const override
+ {
+ return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
+ }
};
}
static std::vector<std::shared_ptr<KeyFilter>> defaultFilters()
{
std::vector<std::shared_ptr<KeyFilter>> result;
result.reserve(6);
result.push_back(std::shared_ptr<KeyFilter>(new MyCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new TrustedCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new FullCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new OtherCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new AllCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new UncertifiedOpenPGPKeysFilter));
result.push_back(std::shared_ptr<KeyFilter>(new KeyNotValidFilter));
return result;
}
class KeyFilterManager::Private
{
public:
Private()
: filters()
, model(this)
{
}
void clear()
{
model.beginResetModel();
filters.clear();
model.endResetModel();
}
std::vector<std::shared_ptr<KeyFilter>> filters;
Model model;
GpgME::Protocol protocol = GpgME::UnknownProtocol;
};
KeyFilterManager *KeyFilterManager::mSelf = nullptr;
KeyFilterManager::KeyFilterManager(QObject *parent)
: QObject(parent)
, d(new Private)
{
mSelf = this;
// ### DF: doesn't a KStaticDeleter work more reliably?
if (QCoreApplication *app = QCoreApplication::instance()) {
connect(app, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
}
reload();
}
KeyFilterManager::~KeyFilterManager()
{
mSelf = nullptr;
if (d) {
d->clear();
}
}
KeyFilterManager *KeyFilterManager::instance()
{
if (!mSelf) {
mSelf = new KeyFilterManager();
}
return mSelf;
}
void KeyFilterManager::alwaysFilterByProtocol(GpgME::Protocol protocol)
{
if (protocol != d->protocol) {
d->protocol = protocol;
reload();
}
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::filterMatching(const Key &key, KeyFilter::MatchContexts contexts) const
{
const auto it = std::find_if(d->filters.cbegin(), d->filters.cend(), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, contexts);
});
if (it != d->filters.cend()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
std::vector<std::shared_ptr<KeyFilter>> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const
{
std::vector<std::shared_ptr<KeyFilter>> result;
result.reserve(d->filters.size());
std::remove_copy_if(d->filters.begin(), d->filters.end(), std::back_inserter(result), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
return !filter->matches(key, contexts);
});
return result;
}
namespace
{
static const auto byDecreasingSpecificity = [](const std::shared_ptr<KeyFilter> &lhs, const std::shared_ptr<KeyFilter> &rhs) {
return lhs->specificity() > rhs->specificity();
};
}
void KeyFilterManager::reload()
{
d->clear();
d->filters = defaultFilters();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
const bool ignoreDeVs = !DeVSCompliance::isCompliant();
for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) {
const KConfigGroup cfg(config, *it);
if (cfg.hasKey("is-de-vs") && ignoreDeVs) {
/* Don't show de-vs filters in other compliance modes */
continue;
}
d->filters.push_back(std::shared_ptr<KeyFilter>(new KConfigBasedKeyFilter(cfg)));
}
std::stable_sort(d->filters.begin(), d->filters.end(), byDecreasingSpecificity);
if (d->protocol != GpgME::UnknownProtocol) {
// remove filters with conflicting isOpenPGP rule
const auto conflictingValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
Kleo::erase_if(d->filters, [conflictingValue](const auto &f) {
const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
Q_ASSERT(filter);
return filter->isOpenPGP() == conflictingValue;
});
// add isOpenPGP rule to all filters
const auto isOpenPGPValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
std::for_each(std::begin(d->filters), std::end(d->filters), [isOpenPGPValue](auto &f) {
const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
Q_ASSERT(filter);
return filter->setIsOpenPGP(isOpenPGPValue);
});
}
qCDebug(LIBKLEO_LOG) << "KeyFilterManager::" << __func__ << "final filter count is" << d->filters.size();
}
QAbstractItemModel *KeyFilterManager::model() const
{
return &d->model;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::keyFilterByID(const QString &id) const
{
const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr<KeyFilter> &filter) {
return filter->id() == id;
});
if (it != d->filters.end()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const
{
if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast<unsigned>(idx.row()) >= d->filters.size()) {
static const std::shared_ptr<KeyFilter> null;
return null;
}
return d->filters[idx.row()];
}
QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr<KeyFilter> &kf) const
{
if (!kf) {
return {};
}
const auto pair = std::equal_range(d->filters.cbegin(), d->filters.cend(), kf, byDecreasingSpecificity);
const auto it = std::find(pair.first, pair.second, kf);
if (it != pair.second) {
return d->model.index(it - d->filters.begin());
} else {
return QModelIndex();
}
}
int Model::rowCount(const QModelIndex &) const
{
return m_keyFilterManagerPrivate->filters.size();
}
QVariant Model::data(const QModelIndex &idx, int role) const
{
if (!idx.isValid() || idx.model() != this || idx.row() < 0 || static_cast<unsigned>(idx.row()) > m_keyFilterManagerPrivate->filters.size()) {
return QVariant();
}
const auto filter = m_keyFilterManagerPrivate->filters[idx.row()];
switch (role) {
case Qt::DecorationRole:
return filter->icon();
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole: /* Most useless tooltip ever. */
return filter->name();
case KeyFilterManager::FilterIdRole:
return filter->id();
case KeyFilterManager::FilterMatchContextsRole:
return QVariant::fromValue(filter->availableMatchContexts());
default:
return QVariant();
}
}
static KeyFilter::FontDescription
get_fontdescription(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, const KeyFilter::FontDescription &initial)
{
return kdtools::accumulate_if(
filters.begin(),
filters.end(),
[&key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance);
},
initial,
[](const KeyFilter::FontDescription &lhs, const std::shared_ptr<KeyFilter> &rhs) {
return lhs.resolve(rhs->fontDescription());
});
}
QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const
{
const KeyFilter::FontDescription fd = get_fontdescription(d->filters, key, KeyFilter::FontDescription());
return fd.font(baseFont);
}
static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QColor (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
});
if (it == filters.cend()) {
return {};
} else {
return (it->get()->*fun)();
}
}
static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const UserID &userID, QColor (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &userID](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(userID, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
});
if (it == filters.cend()) {
return {};
} else {
return (it->get()->*fun)();
}
}
static QString get_string(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QString (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance) && !(filter.get()->*fun)().isEmpty();
});
if (it == filters.cend()) {
return QString();
} else {
return (*it)->icon();
}
}
QColor KeyFilterManager::bgColor(const Key &key) const
{
return get_color(d->filters, key, &KeyFilter::bgColor);
}
QColor KeyFilterManager::fgColor(const Key &key) const
{
return get_color(d->filters, key, &KeyFilter::fgColor);
}
QColor KeyFilterManager::bgColor(const UserID &userID) const
{
return get_color(d->filters, userID, &KeyFilter::bgColor);
}
QColor KeyFilterManager::fgColor(const UserID &userID) const
{
return get_color(d->filters, userID, &KeyFilter::fgColor);
}
QIcon KeyFilterManager::icon(const Key &key) const
{
const QString icon = get_string(d->filters, key, &KeyFilter::icon);
return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon);
}
diff --git a/src/models/useridproxymodel.cpp b/src/models/useridproxymodel.cpp
index 3b7efd05..31bf9deb 100644
--- a/src/models/useridproxymodel.cpp
+++ b/src/models/useridproxymodel.cpp
@@ -1,189 +1,201 @@
/*
SPDX-FileCopyrightText: 2024 g10 Code GmbH
SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "useridproxymodel.h"
#include "keylist.h"
#include "keylistmodel.h"
#include "kleo/keyfiltermanager.h"
#include "utils/formatting.h"
#include "utils/systeminfo.h"
#include <global.h>
#include <QColor>
using namespace Kleo;
UserIDProxyModel::UserIDProxyModel(QObject *parent)
: AbstractKeyListSortFilterProxyModel(parent)
{
}
static QVariant returnIfValid(const QColor &t)
{
if (t.isValid()) {
return t;
} else {
return QVariant();
}
}
QModelIndex UserIDProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if (!sourceIndex.isValid()) {
return {};
}
int row = 0;
for (int i = 0; i < sourceIndex.row(); i++) {
row += userIDsOfSourceRow(i);
}
return index(row, sourceIndex.column(), {});
}
QModelIndex UserIDProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if (!proxyIndex.isValid()) {
return {};
}
return sourceModel()->index(sourceRowForProxyIndex(proxyIndex), proxyIndex.column(), {});
}
int UserIDProxyModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
int sum = 0;
for (int i = 0; i < sourceModel()->rowCount(); i++) {
sum += userIDsOfSourceRow(i);
}
return sum;
}
QModelIndex UserIDProxyModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid()) {
return {};
}
return createIndex(row, column, nullptr);
}
QModelIndex UserIDProxyModel::parent(const QModelIndex &) const
{
return {};
}
int UserIDProxyModel::columnCount(const QModelIndex &index) const
{
return sourceModel()->columnCount(mapToSource(index));
}
QVariant UserIDProxyModel::data(const QModelIndex &index, int role) const
{
const auto row = sourceRowForProxyIndex(index);
const auto offset = sourceOffsetForProxyIndex(index);
const auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
const auto key = model->key(model->index(row, 0));
if (key.isNull()) {
return AbstractKeyListSortFilterProxyModel::data(index, role);
}
const auto userId = key.userID(offset);
if ((role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::AccessibleTextRole)) {
if (index.column() == KeyList::Columns::PrettyName) {
auto name = QString::fromUtf8(userId.name());
if (name.isEmpty()) {
return AbstractKeyListSortFilterProxyModel::data(index, role);
}
return name;
}
if (index.column() == KeyList::Columns::PrettyEMail) {
return QString::fromUtf8(userId.email());
}
if (index.column() == KeyList::Columns::Validity) {
return Formatting::complianceStringShort(userId);
}
if (index.column() == KeyList::Columns::Summary) {
return Formatting::summaryLine(userId);
}
if (index.column() == KeyList::Columns::Origin) {
return Formatting::origin(userId.origin());
}
if (index.column() == KeyList::Columns::LastUpdate) {
if (role == Qt::AccessibleTextRole) {
return Formatting::accessibleDate(userId.lastUpdate());
} else {
return Formatting::dateString(userId.lastUpdate());
}
}
}
if (role == Qt::BackgroundRole) {
if (!SystemInfo::isHighContrastModeActive()) {
return returnIfValid(KeyFilterManager::instance()->bgColor(userId));
}
} else if (role == Qt::ForegroundRole) {
if (!SystemInfo::isHighContrastModeActive()) {
return returnIfValid(KeyFilterManager::instance()->fgColor(userId));
}
}
return AbstractKeyListSortFilterProxyModel::data(index, role);
}
int UserIDProxyModel::sourceRowForProxyIndex(const QModelIndex &index) const
{
int row = index.row();
int i;
for (i = 0; row >= userIDsOfSourceRow(i); i++) {
row -= userIDsOfSourceRow(i);
}
return i;
}
int UserIDProxyModel::sourceOffsetForProxyIndex(const QModelIndex &index) const
{
int row = index.row();
int i;
for (i = 0; row >= userIDsOfSourceRow(i); i++) {
row -= userIDsOfSourceRow(i);
}
auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
auto key = model->key(model->index(sourceRowForProxyIndex(index), 0));
int tmp = row;
for (int j = 0; j <= tmp; j++) {
// account for filtered out S/MIME user IDs
if (key.protocol() == GpgME::Protocol::CMS && !key.userID(j).email()) {
row++;
}
}
return row;
}
int UserIDProxyModel::userIDsOfSourceRow(int sourceRow) const
{
auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
auto key = model->key(model->index(sourceRow, 0));
if (key.protocol() == GpgME::OpenPGP) {
return key.numUserIDs();
}
// Try to filter out some useless SMIME user ids
int count = 0;
const auto &uids = key.userIDs();
for (auto it = uids.begin(); it != uids.end(); ++it) {
const auto &uid = *it;
if (uid.email()) {
count++;
}
}
return count;
}
UserIDProxyModel *UserIDProxyModel::clone() const
{
auto model = new UserIDProxyModel(QObject::parent());
model->setSourceModel(sourceModel());
return model;
}
+
+QModelIndex UserIDProxyModel::index(const KeyGroup &group) const
+{
+ Q_UNUSED(group);
+ return {};
+}
+
+QModelIndex UserIDProxyModel::index(const GpgME::Key &key) const
+{
+ Q_UNUSED(key);
+ return {};
+}
diff --git a/src/models/useridproxymodel.h b/src/models/useridproxymodel.h
index 213c68af..506d2798 100644
--- a/src/models/useridproxymodel.h
+++ b/src/models/useridproxymodel.h
@@ -1,34 +1,36 @@
/*
SPDX-FileCopyrightText: 2024 g10 Code GmbH
SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <Libkleo/KeyListSortFilterProxyModel>
namespace Kleo
{
class KLEO_EXPORT UserIDProxyModel : public Kleo::AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
public:
explicit UserIDProxyModel(QObject *parent = nullptr);
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
int rowCount(const QModelIndex &parent = {}) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
virtual QModelIndex parent(const QModelIndex &) const override;
int columnCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &index, int role) const override;
int sourceRowForProxyIndex(const QModelIndex &index) const;
int sourceOffsetForProxyIndex(const QModelIndex &index) const;
int userIDsOfSourceRow(int sourceRow) const;
UserIDProxyModel *clone() const override;
+ QModelIndex index(const KeyGroup &) const override;
+ QModelIndex index(const GpgME::Key &key) const override;
};
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Feb 1, 9:06 AM (1 d, 4 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ed/11/a337e4aee1d6484eb1c9b8bb9262
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment