Page MenuHome GnuPG

No OneTemporary

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

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

Event Timeline