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",
"Key ID");
341 return i18nc(
"@title:column",
"Fingerprint");
343 return i18nc(
"@title:column",
"Issuer");
345 return i18nc(
"@title:column",
"Serial Number");
347 return i18nc(
"@title:column",
"Origin");
349 return i18nc(
"@title:column",
"Last Update");
351 return i18nc(
"@title:column",
"Certification Trust");
353 return i18nc(
"@title:column",
"Tags");
355 return i18nc(
"@title:column",
"Algorithm");
357 return i18nc(
"@title:column",
"Keygrip");
385 const Key key = this->key(index);
387 return data(key, index.
row(), index.
column(), role);
390 const KeyGroup group = this->group(index);
391 if (!group.isNull()) {
392 return data(group, index.
column(), role);
398QVariant AbstractKeyListModel::data(
const Key &key,
int row,
int column,
int role)
const
403 const auto name = Formatting::prettyName(key);
405 return name.isEmpty() ?
i18nc(
"text for screen readers for an empty name",
"no name") : name;
410 const auto email = d->getEMail(key);
412 return email.
isEmpty() ?
i18nc(
"text for screen readers for an empty email address",
"no email") : email;
417 return Formatting::complianceStringShort(key);
420 return Formatting::creationDate(key);
422 return Formatting::accessibleCreationDate(key);
424 return Formatting::creationDateString(key);
428 return Formatting::expirationDate(key);
430 return Formatting::accessibleExpirationDate(key);
432 return Formatting::expirationDateString(key);
434 case TechnicalDetails:
435 return Formatting::type(key);
438 return Formatting::accessibleHexID(key.shortKeyID());
439 }
else if (role == ClipboardRole) {
442 return Formatting::prettyID(key.shortKeyID());
446 return Formatting::accessibleHexID(key.keyID());
447 }
else if (role == ClipboardRole) {
450 return Formatting::prettyID(key.keyID());
453 return Formatting::summaryLine(key);
456 return Formatting::accessibleHexID(key.primaryFingerprint());
457 }
else if (role == ClipboardRole) {
460 return Formatting::prettyID(key.primaryFingerprint());
465 if (key.origin() == Key::OriginUnknown && (
int)d->extraOrigins.size() > row) {
466 return Formatting::origin(d->extraOrigins[row]);
468 return Formatting::origin(key.origin());
471 return Formatting::accessibleDate(key.lastUpdate());
473 return Formatting::dateString(key.lastUpdate());
478 return Formatting::ownerTrustShort(key.ownerTrust());
480 const char *
const fpr = key.primaryFingerprint();
481 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
482 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
483 return i18n(
"Loading...");
486 if (it != d->remarksCache.constEnd()) {
490 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
491 if (remarks.size() == 1) {
493 return d->remarksCache[fpr] = remark;
496 remarkList.
reserve(remarks.size());
497 for (
const auto &rem : remarks) {
500 const auto remark = remarkList.
join(QStringLiteral(
"; "));
501 return d->remarksCache[fpr] = remark;
510 return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
513 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
521 return Formatting::toolTip(key, toolTipOptions());
523 return KeyFilterManager::instance()->font(key,
524 (column == ShortKeyID || column == KeyID || column == Fingerprint) ?
QFont(QStringLiteral(
"monospace"))
527 return column ==
Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) :
QVariant();
529 if (!SystemInfo::isHighContrastModeActive()) {
530 return returnIfValid(KeyFilterManager::instance()->bgColor(key));
533 if (!SystemInfo::isHighContrastModeActive()) {
534 return returnIfValid(KeyFilterManager::instance()->fgColor(key));
536 }
else if (role == FingerprintRole) {
538 }
else if (role == KeyRole) {
544QVariant AbstractKeyListModel::data(
const KeyGroup &group,
int column,
int role)
const
551 return Formatting::complianceStringShort(group);
552 case TechnicalDetails:
553 return Formatting::type(group);
555 return Formatting::summaryLine(group);
569 return i18nc(
"text for screen readers",
"not applicable");
576 return Formatting::toolTip(group, toolTipOptions());
580 if (column !=
Icon && column != Summary) {
583 return Kleo::all_of(group.keys(),
584 [](
const auto &key) {
585 return key.hasEncrypt();
588 :
QIcon::fromTheme(QStringLiteral(
"emblem-warning"));
591 }
else if (role == GroupRole) {
597bool AbstractKeyListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
602 const KeyGroup group = value.
value<KeyGroup>();
603 return doSetGroupData(index, group);
609bool AbstractKeyListModel::modelResetInProgress()
611 return d->m_modelResetInProgress;
616template<
typename Base>
617class TableModelMixin :
public Base
620 explicit TableModelMixin(
QObject *p =
nullptr)
624 ~TableModelMixin()
override
631 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column,
nullptr) :
QModelIndex();
639 bool hasChildren(
const QModelIndex &pidx)
const override
641 return (pidx.
model() ==
this || !pidx.
isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
645class FlatKeyListModel
647 :
public TableModelMixin<AbstractKeyListModel>
649 :
public AbstractKeyListModel
654 explicit FlatKeyListModel(
QObject *parent =
nullptr);
655 ~FlatKeyListModel()
override;
657 int rowCount(
const QModelIndex &pidx)
const override
659 return pidx.
isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
663 Key doMapToKey(
const QModelIndex &index)
const override;
664 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
666 void doRemoveKey(
const Key &key)
override;
668 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
669 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
670 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
671 QModelIndex doAddGroup(
const KeyGroup &group)
override;
672 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
673 bool doRemoveGroup(
const KeyGroup &group)
override;
675 void doClear(ItemTypes types)
override
678 mKeysByFingerprint.clear();
680 if (types & Groups) {
685 int firstGroupRow()
const
687 return mKeysByFingerprint.size();
690 int lastGroupRow()
const
692 return mKeysByFingerprint.size() + mGroups.size() - 1;
697 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
700 return index.
row() - firstGroupRow();
704 std::vector<Key> mKeysByFingerprint;
705 std::vector<KeyGroup> mGroups;
708class HierarchicalKeyListModel :
public AbstractKeyListModel
712 explicit HierarchicalKeyListModel(
QObject *parent =
nullptr);
713 ~HierarchicalKeyListModel()
override;
715 int rowCount(
const QModelIndex &pidx)
const override;
716 using AbstractKeyListModel::index;
720 bool hasChildren(
const QModelIndex &pidx)
const override
722 return rowCount(pidx) > 0;
726 Key doMapToKey(
const QModelIndex &index)
const override;
727 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
729 void doRemoveKey(
const Key &key)
override;
731 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
732 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
733 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
734 QModelIndex doAddGroup(
const KeyGroup &group)
override;
735 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
736 bool doRemoveGroup(
const KeyGroup &group)
override;
738 void doClear(ItemTypes types)
override;
740 int firstGroupRow()
const
742 return mTopLevels.size();
745 int lastGroupRow()
const
747 return mTopLevels.size() + mGroups.size() - 1;
752 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
755 return index.
row() - firstGroupRow();
759 void addTopLevelKey(
const Key &key);
760 void addKeyWithParent(
const char *issuer_fpr,
const Key &key);
761 void addKeyWithoutParent(
const char *issuer_fpr,
const Key &key);
764 typedef std::map<std::string, std::vector<Key>>
Map;
765 std::vector<Key> mKeysByFingerprint;
766 Map mKeysByExistingParent, mKeysByNonExistingParent;
767 std::vector<Key> mTopLevels;
768 std::vector<KeyGroup> mGroups;
778 static Issuers *instance()
780 static auto self = std::unique_ptr<Issuers>{
new Issuers{}};
784 const char *cleanChainID(
const Key &key)
const
786 const char *chainID =
"";
788 const char *
const chid = key.chainID();
789 if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
796 void maskIssuerOfKey(
const Key &key)
798 mKeysWithMaskedIssuer.insert(key);
803 mKeysWithMaskedIssuer.clear();
807 std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
810static const char *cleanChainID(
const Key &key)
812 return Issuers::instance()->cleanChainID(key);
817FlatKeyListModel::FlatKeyListModel(
QObject *p)
818 : TableModelMixin<AbstractKeyListModel>(p)
822FlatKeyListModel::~FlatKeyListModel()
826Key FlatKeyListModel::doMapToKey(
const QModelIndex &idx)
const
829 if (
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() && idx.
column() < NumColumns) {
830 return mKeysByFingerprint[idx.
row()];
836QModelIndex FlatKeyListModel::doMapFromKey(
const Key &key,
int col)
const
838 Q_ASSERT(!key.isNull());
839 const std::vector<Key>::const_iterator it =
840 std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
841 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
844 return createIndex(it - mKeysByFingerprint.begin(), col);
850 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
856 for (
auto it = keys.begin(), end = keys.end(); it != end; ++it) {
858 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
859 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
861 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
863 mKeysByFingerprint[idx - 1] = *it;
864 if (!modelResetInProgress()) {
865 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
869 if (!modelResetInProgress()) {
872 mKeysByFingerprint.insert(pos, *it);
873 if (!modelResetInProgress()) {
879 return indexes(keys);
882void FlatKeyListModel::doRemoveKey(
const Key &key)
884 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
885 if (it == mKeysByFingerprint.end()) {
889 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
890 if (!modelResetInProgress()) {
893 mKeysByFingerprint.erase(it);
894 if (!modelResetInProgress()) {
899KeyGroup FlatKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
902 if (
static_cast<unsigned>(idx.
row()) >= mKeysByFingerprint.size() &&
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() + mGroups.size()
903 && idx.
column() < NumColumns) {
904 return mGroups[idx.
row() - mKeysByFingerprint.size()];
910QModelIndex FlatKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
912 Q_ASSERT(!group.isNull());
913 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
914 return g.source() == group.source() && g.id() == group.id();
916 if (it == mGroups.cend()) {
919 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
923void FlatKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
925 Q_ASSERT(mGroups.empty());
926 const int first = mKeysByFingerprint.size();
927 const int last = first + groups.size() - 1;
928 if (!modelResetInProgress()) {
932 if (!modelResetInProgress()) {
937QModelIndex FlatKeyListModel::doAddGroup(
const KeyGroup &group)
939 const int newRow = lastGroupRow() + 1;
940 if (!modelResetInProgress()) {
943 mGroups.push_back(group);
944 if (!modelResetInProgress()) {
947 return createIndex(newRow, 0);
950bool FlatKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
952 if (group.isNull()) {
955 const int groupIndex = this->groupIndex(index);
956 if (groupIndex == -1) {
959 mGroups[groupIndex] = group;
960 if (!modelResetInProgress()) {
961 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
966bool FlatKeyListModel::doRemoveGroup(
const KeyGroup &group)
968 const QModelIndex modelIndex = doMapFromGroup(group, 0);
972 const int groupIndex = this->groupIndex(modelIndex);
973 Q_ASSERT(groupIndex != -1);
974 if (groupIndex == -1) {
977 if (!modelResetInProgress()) {
980 mGroups.erase(mGroups.begin() + groupIndex);
981 if (!modelResetInProgress()) {
987HierarchicalKeyListModel::HierarchicalKeyListModel(
QObject *p)
988 : AbstractKeyListModel(p)
989 , mKeysByFingerprint()
990 , mKeysByExistingParent()
991 , mKeysByNonExistingParent()
996HierarchicalKeyListModel::~HierarchicalKeyListModel()
1000int HierarchicalKeyListModel::rowCount(
const QModelIndex &pidx)
const
1004 return mTopLevels.size() + mGroups.size();
1007 if (pidx.
column() != 0) {
1012 const Key issuer = this->key(pidx);
1013 const char *
const fpr = issuer.primaryFingerprint();
1014 if (!fpr || !*fpr) {
1017 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1018 if (it == mKeysByExistingParent.end()) {
1021 return it->second.size();
1026 if (row < 0 || col < 0 || col >= NumColumns) {
1032 if (
static_cast<unsigned>(row) < mTopLevels.size()) {
1033 return index(mTopLevels[row], col);
1034 }
else if (
static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1035 return index(mGroups[row - mTopLevels.size()], col);
1042 const Key issuer = this->key(pidx);
1043 const char *
const fpr = issuer.primaryFingerprint();
1044 if (!fpr || !*fpr) {
1047 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1048 if (it == mKeysByExistingParent.end() ||
static_cast<unsigned>(row) >= it->second.size()) {
1051 return index(it->second[row], col);
1056 const Key key = this->key(idx);
1057 if (key.isNull() || key.isRoot()) {
1060 const std::vector<Key>::const_iterator it =
1061 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1062 return it != mKeysByFingerprint.end() ? index(*it) :
QModelIndex();
1065Key HierarchicalKeyListModel::doMapToKey(
const QModelIndex &idx)
const
1067 Key key = Key::null;
1070 const char *
const issuer_fpr =
static_cast<const char *
>(idx.
internalPointer());
1071 if (!issuer_fpr || !*issuer_fpr) {
1073 if (
static_cast<unsigned>(idx.
row()) < mTopLevels.size()) {
1074 key = mTopLevels[idx.
row()];
1078 const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1079 if (it != mKeysByExistingParent.end() &&
static_cast<unsigned>(idx.
row()) < it->second.size()) {
1080 key = it->second[idx.
row()];
1088QModelIndex HierarchicalKeyListModel::doMapFromKey(
const Key &key,
int col)
const
1094 const char *issuer_fpr = cleanChainID(key);
1097 const std::vector<Key> *v = &mTopLevels;
1098 if (issuer_fpr && *issuer_fpr) {
1099 const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1101 if (it != mKeysByExistingParent.end()) {
1104 issuer_fpr =
nullptr;
1108 const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1109 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1113 const unsigned int row = std::distance(v->begin(), it);
1114 return createIndex(row, col,
const_cast<char *
>(issuer_fpr));
1117void HierarchicalKeyListModel::addKeyWithParent(
const char *issuer_fpr,
const Key &key)
1119 Q_ASSERT(issuer_fpr);
1120 Q_ASSERT(*issuer_fpr);
1121 Q_ASSERT(!key.isNull());
1123 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1126 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1127 const int row = std::distance(subjects.begin(), it);
1129 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1132 if (!modelResetInProgress()) {
1133 Q_EMIT dataChanged(createIndex(row, 0,
const_cast<char *
>(issuer_fpr)), createIndex(row, NumColumns - 1,
const_cast<char *
>(issuer_fpr)));
1137 const std::vector<Key>::const_iterator pos =
1138 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1139 Q_ASSERT(pos != mKeysByFingerprint.end());
1140 if (!modelResetInProgress()) {
1141 beginInsertRows(index(*pos), row, row);
1143 subjects.insert(it, key);
1144 if (!modelResetInProgress()) {
1150void HierarchicalKeyListModel::addKeyWithoutParent(
const char *issuer_fpr,
const Key &key)
1152 Q_ASSERT(issuer_fpr);
1153 Q_ASSERT(*issuer_fpr);
1154 Q_ASSERT(!key.isNull());
1156 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1159 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1161 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1166 subjects.insert(it, key);
1169 addTopLevelKey(key);
1172void HierarchicalKeyListModel::addTopLevelKey(
const Key &key)
1175 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1176 const int row = std::distance(mTopLevels.begin(), it);
1178 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1181 if (!modelResetInProgress()) {
1182 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1186 if (!modelResetInProgress()) {
1189 mTopLevels.insert(it, key);
1190 if (!modelResetInProgress()) {
1200struct cycle_detector :
public boost::dfs_visitor<> {
1201 cycle_detector(
bool &has_cycle)
1202 : _has_cycle{has_cycle}
1206 template<
class Edge,
class Graph>
1207 void back_edge(Edge, Graph &)
1216static bool graph_has_cycle(
const boost::adjacency_list<> &graph)
1218 bool cycle_found =
false;
1219 cycle_detector vis{cycle_found};
1220 boost::depth_first_search(graph, visitor(vis));
1224static void find_keys_causing_cycles_and_mask_their_issuers(
const std::vector<Key> &keys)
1226 boost::adjacency_list<> graph{keys.size()};
1228 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1229 const auto &key = keys[i];
1230 const char *
const issuer_fpr = cleanChainID(key);
1231 if (!issuer_fpr || !*issuer_fpr) {
1234 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1235 if (it == keys.end()) {
1238 const auto j = std::distance(keys.begin(), it);
1239 const auto edge = boost::add_edge(i, j, graph).first;
1240 if (graph_has_cycle(graph)) {
1241 Issuers::instance()->maskIssuerOfKey(key);
1242 boost::remove_edge(edge, graph);
1247static auto build_key_graph(
const std::vector<Key> &keys)
1249 boost::adjacency_list<> graph(keys.size());
1252 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1253 const char *
const issuer_fpr = cleanChainID(keys[i]);
1254 if (!issuer_fpr || !*issuer_fpr) {
1257 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1258 if (it == keys.end()) {
1261 const auto j = std::distance(keys.begin(), it);
1262 add_edge(i, j, graph);
1269static std::vector<Key> topological_sort(
const std::vector<Key> &keys)
1271 const auto graph = build_key_graph(keys);
1273 std::vector<int> order;
1274 order.reserve(keys.size());
1275 topological_sort(graph, std::back_inserter(order));
1277 Q_ASSERT(order.size() == keys.size());
1279 std::vector<Key> result;
1281 for (
int i : std::as_const(order)) {
1282 result.push_back(keys[i]);
1291 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1297 const std::vector<Key> oldKeys = mKeysByFingerprint;
1299 std::vector<Key> merged;
1300 merged.reserve(keys.size() + mKeysByFingerprint.size());
1301 std::set_union(keys.begin(),
1303 mKeysByFingerprint.begin(),
1304 mKeysByFingerprint.end(),
1305 std::back_inserter(merged),
1306 _detail::ByFingerprint<std::less>());
1308 mKeysByFingerprint = merged;
1310 if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1311 find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1314 std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1316 const auto topologicalSortedList = topological_sort(keys);
1317 for (
const Key &key : topologicalSortedList) {
1319 const char *
const fpr = key.primaryFingerprint();
1320 if (!fpr || !*fpr) {
1324 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1326 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1327 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1328 if (it != mKeysByNonExistingParent.end()) {
1329 mKeysByNonExistingParent.erase(it);
1334 if (!keyAlreadyExisted) {
1335 auto last = mTopLevels.begin();
1336 auto lastFP = mKeysByFingerprint.begin();
1338 for (
const Key &k : children) {
1339 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1340 Q_ASSERT(last != mTopLevels.end());
1341 const int row = std::distance(mTopLevels.begin(), last);
1343 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1344 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1347 if (!modelResetInProgress()) {
1350 last = mTopLevels.erase(last);
1351 lastFP = mKeysByFingerprint.erase(lastFP);
1352 if (!modelResetInProgress()) {
1359 const char *
const issuer_fpr = cleanChainID(key);
1360 if (!issuer_fpr || !*issuer_fpr) {
1362 addTopLevelKey(key);
1363 }
else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1365 addKeyWithParent(issuer_fpr, key);
1368 addKeyWithoutParent(issuer_fpr, key);
1373 while (key_parent.
isValid()) {
1374 changedParents.insert(doMapToKey(key_parent));
1375 key_parent = key_parent.
parent();
1380 if (!keyAlreadyExisted && !children.empty()) {
1385 for (
int i = children.size() - 1; i >= 0; --i) {
1386 Q_EMIT rowMoved(new_parent, i);
1392 if (!modelResetInProgress()) {
1393 for (
const Key &i : std::as_const(changedParents)) {
1400 return indexes(keys);
1403void HierarchicalKeyListModel::doRemoveKey(
const Key &key)
1410 const char *
const fpr = key.primaryFingerprint();
1411 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1413 std::vector<Key> keys = mKeysByFingerprint;
1414 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1415 if (it == keys.end()) {
1428 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1430 Q_ASSERT(it != mKeysByFingerprint.end());
1431 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1432 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1434 if (!modelResetInProgress()) {
1435 beginRemoveRows(parent(idx), idx.
row(), idx.
row());
1437 mKeysByFingerprint.erase(it);
1439 const char *
const issuer_fpr = cleanChainID(key);
1441 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1442 if (tlIt != mTopLevels.end()) {
1443 mTopLevels.erase(tlIt);
1446 if (issuer_fpr && *issuer_fpr) {
1447 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1448 if (nexIt != mKeysByNonExistingParent.end()) {
1449 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1450 if (eit != nexIt->second.end()) {
1451 nexIt->second.erase(eit);
1453 if (nexIt->second.empty()) {
1454 mKeysByNonExistingParent.erase(nexIt);
1458 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1459 if (exIt != mKeysByExistingParent.end()) {
1460 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1461 if (eit != exIt->second.end()) {
1462 exIt->second.erase(eit);
1464 if (exIt->second.empty()) {
1465 mKeysByExistingParent.erase(exIt);
1469 if (!modelResetInProgress()) {
1474KeyGroup HierarchicalKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
1482 if (
static_cast<unsigned>(idx.
row()) >= mTopLevels.size() &&
static_cast<unsigned>(idx.
row()) < mTopLevels.size() + mGroups.size()
1483 && idx.
column() < NumColumns) {
1484 return mGroups[idx.
row() - mTopLevels.size()];
1490QModelIndex HierarchicalKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
1492 Q_ASSERT(!group.isNull());
1493 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
1494 return g.source() == group.source() && g.id() == group.id();
1496 if (it == mGroups.cend()) {
1499 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1503void HierarchicalKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
1505 Q_ASSERT(mGroups.empty());
1506 const int first = mTopLevels.size();
1507 const int last = first + groups.size() - 1;
1508 if (!modelResetInProgress()) {
1512 if (!modelResetInProgress()) {
1517QModelIndex HierarchicalKeyListModel::doAddGroup(
const KeyGroup &group)
1519 const int newRow = lastGroupRow() + 1;
1520 if (!modelResetInProgress()) {
1523 mGroups.push_back(group);
1524 if (!modelResetInProgress()) {
1527 return createIndex(newRow, 0);
1530bool HierarchicalKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
1532 if (group.isNull()) {
1535 const int groupIndex = this->groupIndex(index);
1536 if (groupIndex == -1) {
1539 mGroups[groupIndex] = group;
1540 if (!modelResetInProgress()) {
1541 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
1546bool HierarchicalKeyListModel::doRemoveGroup(
const KeyGroup &group)
1548 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1552 const int groupIndex = this->groupIndex(modelIndex);
1553 Q_ASSERT(groupIndex != -1);
1554 if (groupIndex == -1) {
1557 if (!modelResetInProgress()) {
1560 mGroups.erase(mGroups.begin() + groupIndex);
1561 if (!modelResetInProgress()) {
1567void HierarchicalKeyListModel::doClear(ItemTypes types)
1571 mKeysByFingerprint.clear();
1572 mKeysByExistingParent.clear();
1573 mKeysByNonExistingParent.clear();
1574 Issuers::instance()->clear();
1576 if (types & Groups) {
1581void AbstractKeyListModel::useKeyCache(
bool value, KeyList::Options options)
1583 d->m_keyListOptions = options;
1584 d->m_useKeyCache = value;
1585 if (!d->m_useKeyCache) {
1588 d->updateFromKeyCache();
1590 connect(KeyCache::instance().
get(), &KeyCache::keysMayHaveChanged,
this, [
this] {
1591 d->updateFromKeyCache();
1596AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(
QObject *p)
1598 AbstractKeyListModel *
const m =
new FlatKeyListModel(p);
1599#ifdef KLEO_MODEL_TEST
1606AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(
QObject *p)
1608 AbstractKeyListModel *
const m =
new HierarchicalKeyListModel(p);
1609#ifdef KLEO_MODEL_TEST
1615QMimeData *AbstractKeyListModel::mimeData(
const QModelIndexList &indexes)
const
1617 if (d->m_dragHandler) {
1618 return d->m_dragHandler->mimeData(indexes);
1626 if (d->m_dragHandler) {
1627 return d->m_dragHandler->flags(index);
1633QStringList AbstractKeyListModel::mimeTypes()
const
1635 if (d->m_dragHandler) {
1636 return d->m_dragHandler->mimeTypes();
1642void AbstractKeyListModel::setDragHandler(
const std::shared_ptr<DragHandler> &dragHandler)
1644 d->m_dragHandler = dragHandler;
1647#include "keylistmodel.moc"
1686#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)
QAction * clear(const QObject *recvr, const char *slot, QObject *parent)
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)