12#include <config-libkleo.h>
14#include "keylistmodel.h"
18#include <libkleo/algorithm.h>
19#include <libkleo/formatting.h>
20#include <libkleo/keyfilter.h>
21#include <libkleo/keyfiltermanager.h>
22#include <libkleo/predicates.h>
23#include <libkleo/systeminfo.h>
25#include <KLocalizedString>
28#include <QAbstractItemModelTester>
37#include <gpgme++/key.h>
40#include <boost/graph/adjacency_list.hpp>
41#include <boost/graph/topological_sort.hpp>
51using namespace Kleo::KeyList;
54Q_DECLARE_METATYPE(GpgME::Key)
55Q_DECLARE_METATYPE(KeyGroup)
58class AbstractKeyListModel::Private
60 AbstractKeyListModel *
const q;
63 explicit Private(AbstractKeyListModel *qq);
65 void updateFromKeyCache();
67 QString getEMail(
const Key &key)
const;
70 int m_toolTipOptions = Formatting::Validity;
73 bool m_useKeyCache =
false;
74 bool m_modelResetInProgress =
false;
75 KeyList::Options m_keyListOptions = AllKeys;
76 std::vector<GpgME::Key> m_remarkKeys;
77 std::shared_ptr<DragHandler> m_dragHandler;
78 std::vector<Key::Origin> extraOrigins;
81AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
86void AbstractKeyListModel::Private::updateFromKeyCache()
89 const bool inReset = q->modelResetInProgress();
93 q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
94 if (m_keyListOptions == IncludeGroups) {
95 q->setGroups(KeyCache::instance()->groups());
103QString AbstractKeyListModel::Private::getEMail(
const Key &key)
const
106 if (
const auto fpr = key.primaryFingerprint()) {
107 const auto it = prettyEMailCache.constFind(fpr);
108 if (it != prettyEMailCache.constEnd()) {
111 email = Formatting::prettyEMail(key);
112 prettyEMailCache[fpr] = email;
118AbstractKeyListModel::AbstractKeyListModel(
QObject *p)
120 , KeyListModelInterface()
121 , d(new Private(this))
124 d->m_modelResetInProgress =
true;
127 d->m_modelResetInProgress =
false;
131AbstractKeyListModel::~AbstractKeyListModel()
135void AbstractKeyListModel::setToolTipOptions(
int opts)
137 d->m_toolTipOptions = opts;
140int AbstractKeyListModel::toolTipOptions()
const
142 return d->m_toolTipOptions;
145void AbstractKeyListModel::setRemarkKeys(
const std::vector<GpgME::Key> &keys)
147 d->m_remarkKeys = keys;
150std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys()
const
152 return d->m_remarkKeys;
155Key AbstractKeyListModel::key(
const QModelIndex &idx)
const
159 key = doMapToKey(idx);
166 std::vector<Key> result;
167 result.reserve(indexes.
size());
168 std::transform(indexes.
begin(),
170 std::back_inserter(result),
172 return this->key(idx);
174 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
175 _detail::remove_duplicates_by_fpr(result);
179KeyGroup AbstractKeyListModel::group(
const QModelIndex &idx)
const
182 return doMapToGroup(idx);
188QModelIndex AbstractKeyListModel::index(
const Key &key)
const
190 return index(key, 0);
193QModelIndex AbstractKeyListModel::index(
const Key &key,
int col)
const
195 if (key.isNull() || col < 0 || col >= NumColumns) {
198 return doMapFromKey(key, col);
206 std::transform(keys.begin(),
208 std::back_inserter(result),
209 [
this](
const Key &key) {
210 return this->index(key);
215QModelIndex AbstractKeyListModel::index(
const KeyGroup &group)
const
217 return index(group, 0);
220QModelIndex AbstractKeyListModel::index(
const KeyGroup &group,
int col)
const
222 if (group.isNull() || col < 0 || col >= NumColumns) {
225 return doMapFromGroup(group, col);
229void AbstractKeyListModel::setKeys(
const std::vector<Key> &keys,
const std::vector<Key::Origin> &extraOrigins)
231 const bool inReset = modelResetInProgress();
237 d->extraOrigins = extraOrigins;
243QModelIndex AbstractKeyListModel::addKey(
const Key &key)
245 const std::vector<Key> vec(1, key);
250void AbstractKeyListModel::removeKey(
const Key &key)
256 d->prettyEMailCache.remove(key.primaryFingerprint());
257 d->remarksCache.remove(key.primaryFingerprint());
262 std::vector<Key> sorted;
264 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull));
265 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
266 return doAddKeys(sorted);
269void AbstractKeyListModel::setGroups(
const std::vector<KeyGroup> &groups)
271 const bool inReset = modelResetInProgress();
282QModelIndex AbstractKeyListModel::addGroup(
const KeyGroup &group)
284 if (group.isNull()) {
287 return doAddGroup(group);
290bool AbstractKeyListModel::removeGroup(
const KeyGroup &group)
292 if (group.isNull()) {
295 return doRemoveGroup(group);
298void AbstractKeyListModel::clear(ItemTypes types)
300 const bool inReset = modelResetInProgress();
306 d->prettyEMailCache.clear();
307 d->remarksCache.clear();
314int AbstractKeyListModel::columnCount(
const QModelIndex &)
const
325 return i18nc(
"@title:column",
"Name");
327 return i18nc(
"@title:column",
"E-Mail");
329 return i18nc(
"@title:column",
"Status");
331 return i18nc(
"@title:column",
"Valid From");
333 return i18nc(
"@title:column",
"Valid Until");
334 case TechnicalDetails:
335 return i18nc(
"@title:column",
"Protocol");
337 return i18nc(
"@title:column",
"Key ID");
339 return i18nc(
"@title:column",
"Fingerprint");
341 return i18nc(
"@title:column",
"Issuer");
343 return i18nc(
"@title:column",
"Serial Number");
345 return i18nc(
"@title:column",
"Origin");
347 return i18nc(
"@title:column",
"Last Update");
349 return i18nc(
"@title:column",
"Certification Trust");
351 return i18nc(
"@title:column",
"Tags");
353 return i18nc(
"@title:column",
"Algorithm");
355 return i18nc(
"@title:column",
"Keygrip");
383 const Key key = this->key(index);
385 return data(key, index.
row(), index.
column(), role);
388 const KeyGroup group = this->group(index);
389 if (!group.isNull()) {
390 return data(group, index.
column(), role);
396QVariant AbstractKeyListModel::data(
const Key &key,
int row,
int column,
int role)
const
401 const auto name = Formatting::prettyName(key);
403 return name.isEmpty() ?
i18nc(
"text for screen readers for an empty name",
"no name") : name;
408 const auto email = d->getEMail(key);
410 return email.
isEmpty() ?
i18nc(
"text for screen readers for an empty email address",
"no email") : email;
415 return Formatting::complianceStringShort(key);
418 return Formatting::creationDate(key);
420 return Formatting::accessibleCreationDate(key);
422 return Formatting::creationDateString(key);
426 return Formatting::expirationDate(key);
428 return Formatting::accessibleExpirationDate(key);
430 return Formatting::expirationDateString(key);
432 case TechnicalDetails:
433 return Formatting::type(key);
436 return Formatting::accessibleHexID(key.keyID());
437 }
else if (role == ClipboardRole) {
440 return Formatting::prettyID(key.keyID());
443 return Formatting::summaryLine(key);
446 return Formatting::accessibleHexID(key.primaryFingerprint());
447 }
else if (role == ClipboardRole) {
450 return Formatting::prettyID(key.primaryFingerprint());
455 if (key.origin() == Key::OriginUnknown && (
int)d->extraOrigins.size() > row) {
456 return Formatting::origin(d->extraOrigins[row]);
458 return Formatting::origin(key.origin());
461 return Formatting::accessibleDate(key.lastUpdate());
463 return Formatting::dateString(key.lastUpdate());
468 return Formatting::ownerTrustShort(key.ownerTrust());
470 const char *
const fpr = key.primaryFingerprint();
471 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
472 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
473 return i18n(
"Loading...");
476 if (it != d->remarksCache.constEnd()) {
480 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
481 if (remarks.size() == 1) {
483 return d->remarksCache[fpr] = remark;
486 remarkList.
reserve(remarks.size());
487 for (
const auto &rem : remarks) {
490 const auto remark = remarkList.
join(QStringLiteral(
"; "));
491 return d->remarksCache[fpr] = remark;
500 return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
503 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
511 return Formatting::toolTip(key, toolTipOptions());
513 return KeyFilterManager::instance()->font(key, (column == KeyID || column == Fingerprint) ?
QFont(QStringLiteral(
"monospace")) :
QFont());
515 return column ==
Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) :
QVariant();
517 if (!SystemInfo::isHighContrastModeActive()) {
518 return returnIfValid(KeyFilterManager::instance()->bgColor(key));
521 if (!SystemInfo::isHighContrastModeActive()) {
522 return returnIfValid(KeyFilterManager::instance()->fgColor(key));
524 }
else if (role == FingerprintRole) {
526 }
else if (role == KeyRole) {
532QVariant AbstractKeyListModel::data(
const KeyGroup &group,
int column,
int role)
const
539 return Formatting::complianceStringShort(group);
540 case TechnicalDetails:
541 return Formatting::type(group);
543 return Formatting::summaryLine(group);
556 return i18nc(
"text for screen readers",
"not applicable");
563 return Formatting::toolTip(group, toolTipOptions());
567 if (column !=
Icon && column != Summary) {
570 return Kleo::all_of(group.keys(),
571 [](
const auto &key) {
572 return key.hasEncrypt();
575 :
QIcon::fromTheme(QStringLiteral(
"emblem-warning"));
578 }
else if (role == GroupRole) {
584bool AbstractKeyListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
589 const KeyGroup group = value.
value<KeyGroup>();
590 return doSetGroupData(index, group);
596bool AbstractKeyListModel::modelResetInProgress()
598 return d->m_modelResetInProgress;
603template<
typename Base>
604class TableModelMixin :
public Base
607 explicit TableModelMixin(
QObject *p =
nullptr)
611 ~TableModelMixin()
override
618 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column,
nullptr) :
QModelIndex();
626 bool hasChildren(
const QModelIndex &pidx)
const override
628 return (pidx.
model() ==
this || !pidx.
isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
632class FlatKeyListModel
634 :
public TableModelMixin<AbstractKeyListModel>
636 :
public AbstractKeyListModel
641 explicit FlatKeyListModel(
QObject *parent =
nullptr);
642 ~FlatKeyListModel()
override;
644 int rowCount(
const QModelIndex &pidx)
const override
646 return pidx.
isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
650 Key doMapToKey(
const QModelIndex &index)
const override;
651 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
653 void doRemoveKey(
const Key &key)
override;
655 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
656 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
657 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
658 QModelIndex doAddGroup(
const KeyGroup &group)
override;
659 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
660 bool doRemoveGroup(
const KeyGroup &group)
override;
662 void doClear(ItemTypes types)
override
665 mKeysByFingerprint.clear();
667 if (types & Groups) {
672 int firstGroupRow()
const
674 return mKeysByFingerprint.size();
677 int lastGroupRow()
const
679 return mKeysByFingerprint.size() + mGroups.size() - 1;
684 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
687 return index.
row() - firstGroupRow();
691 std::vector<Key> mKeysByFingerprint;
692 std::vector<KeyGroup> mGroups;
695class HierarchicalKeyListModel :
public AbstractKeyListModel
699 explicit HierarchicalKeyListModel(
QObject *parent =
nullptr);
700 ~HierarchicalKeyListModel()
override;
702 int rowCount(
const QModelIndex &pidx)
const override;
703 using AbstractKeyListModel::index;
707 bool hasChildren(
const QModelIndex &pidx)
const override
709 return rowCount(pidx) > 0;
713 Key doMapToKey(
const QModelIndex &index)
const override;
714 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
716 void doRemoveKey(
const Key &key)
override;
718 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
719 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
720 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
721 QModelIndex doAddGroup(
const KeyGroup &group)
override;
722 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
723 bool doRemoveGroup(
const KeyGroup &group)
override;
725 void doClear(ItemTypes types)
override;
727 int firstGroupRow()
const
729 return mTopLevels.size();
732 int lastGroupRow()
const
734 return mTopLevels.size() + mGroups.size() - 1;
739 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
742 return index.
row() - firstGroupRow();
746 void addTopLevelKey(
const Key &key);
747 void addKeyWithParent(
const char *issuer_fpr,
const Key &key);
748 void addKeyWithoutParent(
const char *issuer_fpr,
const Key &key);
751 typedef std::map<std::string, std::vector<Key>>
Map;
752 std::vector<Key> mKeysByFingerprint;
753 Map mKeysByExistingParent, mKeysByNonExistingParent;
754 std::vector<Key> mTopLevels;
755 std::vector<KeyGroup> mGroups;
765 static Issuers *instance()
767 static auto self = std::unique_ptr<Issuers>{
new Issuers{}};
771 const char *cleanChainID(
const Key &key)
const
773 const char *chainID =
"";
775 const char *
const chid = key.chainID();
776 if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
783 void maskIssuerOfKey(
const Key &key)
785 mKeysWithMaskedIssuer.insert(key);
790 mKeysWithMaskedIssuer.clear();
794 std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
797static const char *cleanChainID(
const Key &key)
799 return Issuers::instance()->cleanChainID(key);
804FlatKeyListModel::FlatKeyListModel(
QObject *p)
805 : TableModelMixin<AbstractKeyListModel>(p)
809FlatKeyListModel::~FlatKeyListModel()
813Key FlatKeyListModel::doMapToKey(
const QModelIndex &idx)
const
816 if (
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() && idx.
column() < NumColumns) {
817 return mKeysByFingerprint[idx.
row()];
823QModelIndex FlatKeyListModel::doMapFromKey(
const Key &key,
int col)
const
825 Q_ASSERT(!key.isNull());
826 const std::vector<Key>::const_iterator it =
827 std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
828 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
831 return createIndex(it - mKeysByFingerprint.begin(), col);
837 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
843 for (
auto it = keys.begin(), end = keys.end(); it != end; ++it) {
845 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
846 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
848 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
850 mKeysByFingerprint[idx - 1] = *it;
851 if (!modelResetInProgress()) {
852 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
856 if (!modelResetInProgress()) {
859 mKeysByFingerprint.insert(pos, *it);
860 if (!modelResetInProgress()) {
866 return indexes(keys);
869void FlatKeyListModel::doRemoveKey(
const Key &key)
871 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
872 if (it == mKeysByFingerprint.end()) {
876 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
877 if (!modelResetInProgress()) {
880 mKeysByFingerprint.erase(it);
881 if (!modelResetInProgress()) {
886KeyGroup FlatKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
889 if (
static_cast<unsigned>(idx.
row()) >= mKeysByFingerprint.size() &&
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() + mGroups.size()
890 && idx.
column() < NumColumns) {
891 return mGroups[idx.
row() - mKeysByFingerprint.size()];
897QModelIndex FlatKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
899 Q_ASSERT(!group.isNull());
900 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
901 return g.source() == group.source() && g.id() == group.id();
903 if (it == mGroups.cend()) {
906 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
910void FlatKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
912 Q_ASSERT(mGroups.empty());
913 const int first = mKeysByFingerprint.size();
914 const int last = first + groups.size() - 1;
915 if (!modelResetInProgress()) {
919 if (!modelResetInProgress()) {
924QModelIndex FlatKeyListModel::doAddGroup(
const KeyGroup &group)
926 const int newRow = lastGroupRow() + 1;
927 if (!modelResetInProgress()) {
930 mGroups.push_back(group);
931 if (!modelResetInProgress()) {
934 return createIndex(newRow, 0);
937bool FlatKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
939 if (group.isNull()) {
942 const int groupIndex = this->groupIndex(index);
943 if (groupIndex == -1) {
946 mGroups[groupIndex] = group;
947 if (!modelResetInProgress()) {
948 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
953bool FlatKeyListModel::doRemoveGroup(
const KeyGroup &group)
955 const QModelIndex modelIndex = doMapFromGroup(group, 0);
959 const int groupIndex = this->groupIndex(modelIndex);
960 Q_ASSERT(groupIndex != -1);
961 if (groupIndex == -1) {
964 if (!modelResetInProgress()) {
967 mGroups.erase(mGroups.begin() + groupIndex);
968 if (!modelResetInProgress()) {
974HierarchicalKeyListModel::HierarchicalKeyListModel(
QObject *p)
975 : AbstractKeyListModel(p)
976 , mKeysByFingerprint()
977 , mKeysByExistingParent()
978 , mKeysByNonExistingParent()
983HierarchicalKeyListModel::~HierarchicalKeyListModel()
987int HierarchicalKeyListModel::rowCount(
const QModelIndex &pidx)
const
991 return mTopLevels.size() + mGroups.size();
999 const Key issuer = this->key(pidx);
1000 const char *
const fpr = issuer.primaryFingerprint();
1001 if (!fpr || !*fpr) {
1004 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1005 if (it == mKeysByExistingParent.end()) {
1008 return it->second.size();
1013 if (row < 0 || col < 0 || col >= NumColumns) {
1019 if (
static_cast<unsigned>(row) < mTopLevels.size()) {
1020 return index(mTopLevels[row], col);
1021 }
else if (
static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1022 return index(mGroups[row - mTopLevels.size()], col);
1029 const Key issuer = this->key(pidx);
1030 const char *
const fpr = issuer.primaryFingerprint();
1031 if (!fpr || !*fpr) {
1034 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1035 if (it == mKeysByExistingParent.end() ||
static_cast<unsigned>(row) >= it->second.size()) {
1038 return index(it->second[row], col);
1043 const Key key = this->key(idx);
1044 if (key.isNull() || key.isRoot()) {
1047 const std::vector<Key>::const_iterator it =
1048 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1049 return it != mKeysByFingerprint.end() ? index(*it) :
QModelIndex();
1052Key HierarchicalKeyListModel::doMapToKey(
const QModelIndex &idx)
const
1054 Key key = Key::null;
1057 const char *
const issuer_fpr =
static_cast<const char *
>(idx.
internalPointer());
1058 if (!issuer_fpr || !*issuer_fpr) {
1060 if (
static_cast<unsigned>(idx.
row()) < mTopLevels.size()) {
1061 key = mTopLevels[idx.
row()];
1065 const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1066 if (it != mKeysByExistingParent.end() &&
static_cast<unsigned>(idx.
row()) < it->second.size()) {
1067 key = it->second[idx.
row()];
1075QModelIndex HierarchicalKeyListModel::doMapFromKey(
const Key &key,
int col)
const
1081 const char *issuer_fpr = cleanChainID(key);
1084 const std::vector<Key> *v = &mTopLevels;
1085 if (issuer_fpr && *issuer_fpr) {
1086 const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1088 if (it != mKeysByExistingParent.end()) {
1091 issuer_fpr =
nullptr;
1095 const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1096 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1100 const unsigned int row = std::distance(v->begin(), it);
1101 return createIndex(row, col,
const_cast<char *
>(issuer_fpr));
1104void HierarchicalKeyListModel::addKeyWithParent(
const char *issuer_fpr,
const Key &key)
1106 Q_ASSERT(issuer_fpr);
1107 Q_ASSERT(*issuer_fpr);
1108 Q_ASSERT(!key.isNull());
1110 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1113 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1114 const int row = std::distance(subjects.begin(), it);
1116 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1119 if (!modelResetInProgress()) {
1120 Q_EMIT dataChanged(createIndex(row, 0,
const_cast<char *
>(issuer_fpr)), createIndex(row, NumColumns - 1,
const_cast<char *
>(issuer_fpr)));
1124 const std::vector<Key>::const_iterator pos =
1125 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1126 Q_ASSERT(pos != mKeysByFingerprint.end());
1127 if (!modelResetInProgress()) {
1128 beginInsertRows(index(*pos), row, row);
1130 subjects.insert(it, key);
1131 if (!modelResetInProgress()) {
1137void HierarchicalKeyListModel::addKeyWithoutParent(
const char *issuer_fpr,
const Key &key)
1139 Q_ASSERT(issuer_fpr);
1140 Q_ASSERT(*issuer_fpr);
1141 Q_ASSERT(!key.isNull());
1143 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1146 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1148 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1153 subjects.insert(it, key);
1156 addTopLevelKey(key);
1159void HierarchicalKeyListModel::addTopLevelKey(
const Key &key)
1162 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1163 const int row = std::distance(mTopLevels.begin(), it);
1165 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1168 if (!modelResetInProgress()) {
1169 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1173 if (!modelResetInProgress()) {
1176 mTopLevels.insert(it, key);
1177 if (!modelResetInProgress()) {
1187struct cycle_detector :
public boost::dfs_visitor<> {
1188 cycle_detector(
bool &has_cycle)
1189 : _has_cycle{has_cycle}
1193 template<
class Edge,
class Graph>
1194 void back_edge(Edge, Graph &)
1203static bool graph_has_cycle(
const boost::adjacency_list<> &graph)
1205 bool cycle_found =
false;
1206 cycle_detector vis{cycle_found};
1207 boost::depth_first_search(graph, visitor(vis));
1211static void find_keys_causing_cycles_and_mask_their_issuers(
const std::vector<Key> &keys)
1213 boost::adjacency_list<> graph{keys.size()};
1215 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1216 const auto &key = keys[i];
1217 const char *
const issuer_fpr = cleanChainID(key);
1218 if (!issuer_fpr || !*issuer_fpr) {
1221 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1222 if (it == keys.end()) {
1225 const auto j = std::distance(keys.begin(), it);
1226 const auto edge = boost::add_edge(i, j, graph).first;
1227 if (graph_has_cycle(graph)) {
1228 Issuers::instance()->maskIssuerOfKey(key);
1229 boost::remove_edge(edge, graph);
1234static auto build_key_graph(
const std::vector<Key> &keys)
1236 boost::adjacency_list<> graph(keys.size());
1239 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1240 const char *
const issuer_fpr = cleanChainID(keys[i]);
1241 if (!issuer_fpr || !*issuer_fpr) {
1244 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1245 if (it == keys.end()) {
1248 const auto j = std::distance(keys.begin(), it);
1249 add_edge(i, j, graph);
1256static std::vector<Key> topological_sort(
const std::vector<Key> &keys)
1258 const auto graph = build_key_graph(keys);
1260 std::vector<int> order;
1261 order.reserve(keys.size());
1262 topological_sort(graph, std::back_inserter(order));
1264 Q_ASSERT(order.size() == keys.size());
1266 std::vector<Key> result;
1268 for (
int i : std::as_const(order)) {
1269 result.push_back(keys[i]);
1278 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1284 const std::vector<Key> oldKeys = mKeysByFingerprint;
1286 std::vector<Key> merged;
1287 merged.reserve(keys.size() + mKeysByFingerprint.size());
1288 std::set_union(keys.begin(),
1290 mKeysByFingerprint.begin(),
1291 mKeysByFingerprint.end(),
1292 std::back_inserter(merged),
1293 _detail::ByFingerprint<std::less>());
1295 mKeysByFingerprint = merged;
1297 if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1298 find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1301 std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1303 const auto topologicalSortedList = topological_sort(keys);
1304 for (
const Key &key : topologicalSortedList) {
1306 const char *
const fpr = key.primaryFingerprint();
1307 if (!fpr || !*fpr) {
1311 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1313 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1314 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1315 if (it != mKeysByNonExistingParent.end()) {
1316 mKeysByNonExistingParent.erase(it);
1321 if (!keyAlreadyExisted) {
1322 auto last = mTopLevels.begin();
1323 auto lastFP = mKeysByFingerprint.begin();
1325 for (
const Key &k : children) {
1326 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1327 Q_ASSERT(last != mTopLevels.end());
1328 const int row = std::distance(mTopLevels.begin(), last);
1330 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1331 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1334 if (!modelResetInProgress()) {
1337 last = mTopLevels.erase(last);
1338 lastFP = mKeysByFingerprint.erase(lastFP);
1339 if (!modelResetInProgress()) {
1346 const char *
const issuer_fpr = cleanChainID(key);
1347 if (!issuer_fpr || !*issuer_fpr) {
1349 addTopLevelKey(key);
1350 }
else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1352 addKeyWithParent(issuer_fpr, key);
1355 addKeyWithoutParent(issuer_fpr, key);
1360 while (key_parent.
isValid()) {
1361 changedParents.insert(doMapToKey(key_parent));
1362 key_parent = key_parent.
parent();
1367 if (!keyAlreadyExisted && !children.empty()) {
1372 for (
int i = children.size() - 1; i >= 0; --i) {
1373 Q_EMIT rowMoved(new_parent, i);
1379 if (!modelResetInProgress()) {
1380 for (
const Key &i : std::as_const(changedParents)) {
1387 return indexes(keys);
1390void HierarchicalKeyListModel::doRemoveKey(
const Key &key)
1397 const char *
const fpr = key.primaryFingerprint();
1398 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1400 std::vector<Key> keys = mKeysByFingerprint;
1401 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1402 if (it == keys.end()) {
1415 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1417 Q_ASSERT(it != mKeysByFingerprint.end());
1418 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1419 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1421 if (!modelResetInProgress()) {
1422 beginRemoveRows(parent(idx), idx.
row(), idx.
row());
1424 mKeysByFingerprint.erase(it);
1426 const char *
const issuer_fpr = cleanChainID(key);
1428 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1429 if (tlIt != mTopLevels.end()) {
1430 mTopLevels.erase(tlIt);
1433 if (issuer_fpr && *issuer_fpr) {
1434 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1435 if (nexIt != mKeysByNonExistingParent.end()) {
1436 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1437 if (eit != nexIt->second.end()) {
1438 nexIt->second.erase(eit);
1440 if (nexIt->second.empty()) {
1441 mKeysByNonExistingParent.erase(nexIt);
1445 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1446 if (exIt != mKeysByExistingParent.end()) {
1447 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1448 if (eit != exIt->second.end()) {
1449 exIt->second.erase(eit);
1451 if (exIt->second.empty()) {
1452 mKeysByExistingParent.erase(exIt);
1456 if (!modelResetInProgress()) {
1461KeyGroup HierarchicalKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
1469 if (
static_cast<unsigned>(idx.
row()) >= mTopLevels.size() &&
static_cast<unsigned>(idx.
row()) < mTopLevels.size() + mGroups.size()
1470 && idx.
column() < NumColumns) {
1471 return mGroups[idx.
row() - mTopLevels.size()];
1477QModelIndex HierarchicalKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
1479 Q_ASSERT(!group.isNull());
1480 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
1481 return g.source() == group.source() && g.id() == group.id();
1483 if (it == mGroups.cend()) {
1486 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1490void HierarchicalKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
1492 Q_ASSERT(mGroups.empty());
1493 const int first = mTopLevels.size();
1494 const int last = first + groups.size() - 1;
1495 if (!modelResetInProgress()) {
1499 if (!modelResetInProgress()) {
1504QModelIndex HierarchicalKeyListModel::doAddGroup(
const KeyGroup &group)
1506 const int newRow = lastGroupRow() + 1;
1507 if (!modelResetInProgress()) {
1510 mGroups.push_back(group);
1511 if (!modelResetInProgress()) {
1514 return createIndex(newRow, 0);
1517bool HierarchicalKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
1519 if (group.isNull()) {
1522 const int groupIndex = this->groupIndex(index);
1523 if (groupIndex == -1) {
1526 mGroups[groupIndex] = group;
1527 if (!modelResetInProgress()) {
1528 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
1533bool HierarchicalKeyListModel::doRemoveGroup(
const KeyGroup &group)
1535 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1539 const int groupIndex = this->groupIndex(modelIndex);
1540 Q_ASSERT(groupIndex != -1);
1541 if (groupIndex == -1) {
1544 if (!modelResetInProgress()) {
1547 mGroups.erase(mGroups.begin() + groupIndex);
1548 if (!modelResetInProgress()) {
1554void HierarchicalKeyListModel::doClear(ItemTypes types)
1558 mKeysByFingerprint.clear();
1559 mKeysByExistingParent.clear();
1560 mKeysByNonExistingParent.clear();
1561 Issuers::instance()->clear();
1563 if (types & Groups) {
1568void AbstractKeyListModel::useKeyCache(
bool value, KeyList::Options options)
1570 d->m_keyListOptions = options;
1571 d->m_useKeyCache = value;
1572 if (!d->m_useKeyCache) {
1575 d->updateFromKeyCache();
1577 connect(KeyCache::instance().
get(), &KeyCache::keysMayHaveChanged,
this, [
this] {
1578 d->updateFromKeyCache();
1583AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(
QObject *p)
1585 AbstractKeyListModel *
const m =
new FlatKeyListModel(p);
1586#ifdef KLEO_MODEL_TEST
1593AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(
QObject *p)
1595 AbstractKeyListModel *
const m =
new HierarchicalKeyListModel(p);
1596#ifdef KLEO_MODEL_TEST
1602QMimeData *AbstractKeyListModel::mimeData(
const QModelIndexList &indexes)
const
1604 if (d->m_dragHandler) {
1605 return d->m_dragHandler->mimeData(indexes);
1613 if (d->m_dragHandler) {
1614 return d->m_dragHandler->flags(index);
1620QStringList AbstractKeyListModel::mimeTypes()
const
1622 if (d->m_dragHandler) {
1623 return d->m_dragHandler->mimeTypes();
1629void AbstractKeyListModel::setDragHandler(
const std::shared_ptr<DragHandler> &dragHandler)
1631 d->m_dragHandler = dragHandler;
1634#include "keylistmodel.moc"
1673#include "moc_keylistmodel.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const
virtual QStringList mimeTypes() const const
void modelAboutToBeReset()
bool isValid() const const
QIcon fromTheme(const QString &name)
bool isNull() const const
void reserve(qsizetype size)
qsizetype size() const const
void * internalPointer() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
QModelIndex sibling(int row, int column) const const
QString fromLatin1(QByteArrayView str)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString join(QChar separator) const const
QTestData & newRow(const char *dataTag)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool canConvert() const const
QVariant fromValue(T &&value)