Commit 72ef0d9f authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Add support for groups of keys with identical names

Groups of keys with identical names can come from different sources,
e.g. gpg.conf and the group configuration of the application. Groups
of keys with identical names, but different ids, can come from the
group configuration of the application.

Make the key list models work with groups of keys with identical names
by looking up groups by source, id, and name.
Make the KeyCache work with groups of keys with identical names by
allowing multiple groups per name.

GnuPG-bug-id: 5175
parent 7acaab74
......@@ -71,15 +71,15 @@ void AbstractKeyListModelTest::testSetGroups()
QScopedPointer<AbstractKeyListModel> model(createModel());
const std::vector<KeyGroup> groups = {
KeyGroup("test1", std::vector<Key>())
KeyGroup("test1", "test1", std::vector<Key>(), KeyGroup::UnknownSource)
};
model->setGroups(groups);
QCOMPARE( model->rowCount(), 1 );
QVERIFY( model->index(groups[0]).isValid() );
const std::vector<KeyGroup> otherGroups = {
KeyGroup("test2", std::vector<Key>()),
KeyGroup("test3", std::vector<Key>())
KeyGroup("test2", "test2", std::vector<Key>(), KeyGroup::UnknownSource),
KeyGroup("test3", "test3", std::vector<Key>(), KeyGroup::UnknownSource)
};
model->setGroups(otherGroups);
QCOMPARE( model->rowCount(), 2 );
......@@ -93,7 +93,7 @@ void AbstractKeyListModelTest::testKeys()
QScopedPointer<AbstractKeyListModel> model(createModel());
const Key key = createTestKey("test@example.net");
const KeyGroup group("test", {key});
const KeyGroup group("test", "test", {key}, KeyGroup::UnknownSource);
model->setKeys({key});
model->setGroups({group});
......@@ -135,10 +135,15 @@ void AbstractKeyListModelTest::testIndex()
QScopedPointer<AbstractKeyListModel> model(createModel());
const Key key = createTestKey("test@example.net");
const KeyGroup group("test", {key});
const std::vector<KeyGroup> groups = {
KeyGroup("test", "test", {key}, KeyGroup::UnknownSource),
KeyGroup("test", "test", {key}, KeyGroup::GnuPGConfig),
KeyGroup("test", "test", {key}, KeyGroup::ApplicationConfig),
KeyGroup("otherId", "test", {key}, KeyGroup::UnknownSource)
};
model->setKeys({key});
model->setGroups({group});
model->setGroups(groups);
const QModelIndex keyIndex = model->index(0, 0);
QVERIFY( keyIndex.isValid() );
......@@ -149,11 +154,35 @@ void AbstractKeyListModelTest::testIndex()
QVERIFY( !model->group(groupIndex).isNull() );
}
void AbstractKeyListModelTest::testIndexForGroup()
{
QScopedPointer<AbstractKeyListModel> model(createModel());
const Key key = createTestKey("test@example.net");
const std::vector<KeyGroup> groups = {
KeyGroup("test", "test", {key}, KeyGroup::UnknownSource),
KeyGroup("test", "test", {key}, KeyGroup::GnuPGConfig),
KeyGroup("test", "test", {key}, KeyGroup::ApplicationConfig),
KeyGroup("otherId", "test", {key}, KeyGroup::UnknownSource)
};
model->setKeys({key});
model->setGroups(groups);
QSet<int> rows;
for (const KeyGroup &group : groups) {
const QModelIndex groupIndex = model->index(group);
QVERIFY( groupIndex.isValid() );
rows.insert(groupIndex.row());
}
QCOMPARE(rows.size(), 4);
}
void AbstractKeyListModelTest::testClear()
{
QScopedPointer<AbstractKeyListModel> model(createModel());
const KeyGroup group("test", std::vector<Key>());
const KeyGroup group("test", "test", std::vector<Key>(), KeyGroup::UnknownSource);
model->setGroups({group});
model->clear(AbstractKeyListModel::Keys);
......
......@@ -27,6 +27,7 @@ private Q_SLOTS:
void testSetGroups();
void testKeys();
void testIndex();
void testIndexForGroup();
void testClear();
private:
......
......@@ -20,27 +20,31 @@ using namespace GpgME;
class KeyGroup::Private
{
public:
explicit Private(const QString &name, const std::vector<GpgME::Key> &keys);
explicit Private(const QString &id, const QString &name, const std::vector<Key> &keys, Source source);
QString id;
QString name;
std::vector<Key> keys;
Source source;
};
KeyGroup::Private::Private(const QString &name_, const std::vector<GpgME::Key> &keys_)
: name(name_)
KeyGroup::Private::Private(const QString &id_, const QString &name_, const std::vector<Key> &keys_, Source source_)
: id(id_)
, name(name_)
, keys(keys_)
, source(source_)
{
}
KeyGroup::KeyGroup()
: KeyGroup(QString(), {})
: KeyGroup(QString(), QString(), {}, UnknownSource)
{
}
KeyGroup::~KeyGroup() = default;
KeyGroup::KeyGroup(const QString &name, const std::vector<GpgME::Key> &keys)
: d(new Private(name, keys))
KeyGroup::KeyGroup(const QString &id, const QString &name, const std::vector<Key> &keys, Source source)
: d(new Private(id, name, keys, source))
{
}
......@@ -61,7 +65,12 @@ KeyGroup &KeyGroup::operator=(KeyGroup &&other) = default;
bool KeyGroup::isNull() const
{
return !d || d->name.isEmpty();
return !d || d->id.isEmpty();
}
QString KeyGroup::id() const
{
return d ? d->id : QString();
}
QString KeyGroup::name() const
......@@ -74,3 +83,8 @@ const std::vector<Key> &KeyGroup::keys() const
static const std::vector<Key> empty;
return d ? d->keys : empty;
}
KeyGroup::Source KeyGroup::source() const
{
return d ? d->source : UnknownSource;
}
......@@ -29,10 +29,17 @@ namespace Kleo
class KLEO_EXPORT KeyGroup
{
public:
enum Source {
UnknownSource,
ApplicationConfig,
GnuPGConfig,
Tags
};
KeyGroup();
~KeyGroup();
explicit KeyGroup(const QString &name, const std::vector<GpgME::Key> &keys);
explicit KeyGroup(const QString &id, const QString &name, const std::vector<GpgME::Key> &keys, Source source);
KeyGroup(const KeyGroup &other);
KeyGroup &operator=(const KeyGroup &other);
......@@ -42,9 +49,10 @@ public:
bool isNull() const;
QString id() const;
QString name() const;
const std::vector<GpgME::Key> &keys() const;
Source source() const;
private:
class Private;
......
......@@ -186,7 +186,22 @@ public:
void ensureCachePopulated() const;
void readGroupsFromGpgConf(QMap<QString, KeyGroup> &groups)
std::vector<Key> getKeysForGroup(const QString &groupName, const QStringList &fingerprints)
{
std::vector<Key> groupKeys;
groupKeys.reserve(fingerprints.size());
for (const QString &fpr : fingerprints) {
const Key key = q->findByFingerprint(fpr.toLatin1().constData());
if (key.isNull()) {
qCDebug (LIBKLEO_LOG) << "Ignoring unknown fingerprint:" << fpr << "in group" << groupName;
continue;
}
groupKeys.push_back(key);
}
return groupKeys;
}
void readGroupsFromGpgConf(QMultiMap<QString, KeyGroup> &groups)
{
// According to Werner Koch groups are more of a hack to solve
// a valid usecase (e.g. several keys defined for an internal mailing list)
......@@ -204,25 +219,28 @@ public:
return;
}
for (const auto &value: entry->stringValueList()) {
const auto split = value.split(QLatin1Char('='));
// collect the key fingerprints for all groups read from the configuration
QMap<QString, QStringList> fingerprints;
for (const QString &value: entry->stringValueList()) {
const QStringList split = value.split(QLatin1Char('='));
if (split.size() != 2) {
qCDebug (LIBKLEO_LOG) << "Ignoring invalid group config:" << value;
continue;
}
const auto name = split[0];
const auto key = q->findByFingerprint(split[1].toLatin1().constData());
if (key.isNull()) {
qCDebug (LIBKLEO_LOG) << "Ignoring unknown fingerprint:" << split[1] << "in group" << name;
continue;
}
std::vector<Key> groupKeys = groups.value(name).keys();
groupKeys.push_back(key);
groups.insert(name, KeyGroup(name, groupKeys));
const QString groupName = split[0];
const QString fingerprint = split[1];
fingerprints[groupName].push_back(fingerprint);
}
// add all groups read from the configuration to the list of groups
for (auto it = fingerprints.cbegin(); it != fingerprints.cend(); ++it) {
const QString groupName = it.key();
const std::vector<Key> groupKeys = getKeysForGroup(groupName, it.value());
groups.insert(groupName, KeyGroup(groupName, groupName, groupKeys, KeyGroup::GnuPGConfig));
}
}
void readGroupsFromGroupsConfig(QMap<QString, KeyGroup> &groups)
void readGroupsFromGroupsConfig(QMultiMap<QString, KeyGroup> &groups)
{
static const QString groupNamePrefix = QStringLiteral("Group-");
......@@ -247,18 +265,9 @@ public:
continue;
}
const QStringList fingerprints = configGroup.readEntry("Keys", QStringList());
std::vector<Key> groupKeys;
groupKeys.reserve(fingerprints.size());
for (const QString &fpr : fingerprints) {
const Key key = q->findByFingerprint(fpr.toLatin1().constData());
if (key.isNull()) {
qCDebug (LIBKLEO_LOG) << "Ignoring unknown fingerprint:" << fpr << "in group" << keyGroupName;
continue;
}
groupKeys.push_back(key);
}
const std::vector<Key> groupKeys = getKeysForGroup(keyGroupName, fingerprints);
qCDebug(LIBKLEO_LOG) << "Read group with id" << keyGroupId << ", name" << keyGroupName << ", and keys" << fingerprints;
groups.insert(keyGroupName, KeyGroup(keyGroupName, groupKeys));
groups.insert(keyGroupName, KeyGroup(keyGroupId, keyGroupName, groupKeys, KeyGroup::ApplicationConfig));
}
}
}
......@@ -289,7 +298,7 @@ private:
bool m_pgpOnly;
bool m_remarks_enabled;
QString m_groupsConfigName;
QMap<QString, KeyGroup> m_groups;
QMultiMap<QString, KeyGroup> m_groups;
};
std::shared_ptr<const KeyCache> KeyCache::instance()
......@@ -1423,9 +1432,14 @@ std::vector<GpgME::Key> KeyCache::findBestByMailBox(const char *addr, GpgME::Pro
return ret;
}
std::vector<GpgME::Key> KeyCache::getGroupKeys(const QString &groupName) const
std::vector<Key> KeyCache::getGroupKeys(const QString &groupName) const
{
return d->m_groups.value(groupName).keys();
std::vector<Key> result;
for (auto it = d->m_groups.find(groupName); it != d->m_groups.end() && it.key() == groupName; ++it) {
const std::vector<Key> &keys = it.value().keys();
std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
}
return result;
}
#include "moc_keycache_p.cpp"
......
......@@ -685,8 +685,12 @@ KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const
QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
{
Q_ASSERT(!group.isNull());
const QString name = group.name();
const auto it = std::find_if(mGroups.begin(), mGroups.end(), [name](const KeyGroup &g) { return g.name() == name; });
const auto it = std::find_if(mGroups.begin(), mGroups.end(),
[group](const KeyGroup &g) {
return g.source() == group.source()
&& g.id() == group.id()
&& g.name() == group.name();
});
if (it == mGroups.end()) {
return QModelIndex();
} else {
......@@ -1138,8 +1142,12 @@ KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const
QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
{
Q_ASSERT(!group.isNull());
const QString name = group.name();
const auto it = std::find_if(mGroups.begin(), mGroups.end(), [name](const KeyGroup &g) { return g.name() == name; });
const auto it = std::find_if(mGroups.begin(), mGroups.end(),
[group](const KeyGroup &g) {
return g.source() == group.source()
&& g.id() == group.id()
&& g.name() == group.name();
});
if (it == mGroups.end()) {
return QModelIndex();
} else {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment