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;
80AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
85void AbstractKeyListModel::Private::updateFromKeyCache()
88 const bool inReset = q->modelResetInProgress();
92 q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
93 if (m_keyListOptions == IncludeGroups) {
94 q->setGroups(KeyCache::instance()->groups());
102QString AbstractKeyListModel::Private::getEMail(
const Key &key)
const
105 if (
const auto fpr = key.primaryFingerprint()) {
106 const auto it = prettyEMailCache.constFind(fpr);
107 if (it != prettyEMailCache.constEnd()) {
110 email = Formatting::prettyEMail(key);
111 prettyEMailCache[fpr] = email;
117AbstractKeyListModel::AbstractKeyListModel(
QObject *p)
119 , KeyListModelInterface()
120 , d(new Private(this))
123 d->m_modelResetInProgress =
true;
126 d->m_modelResetInProgress =
false;
130AbstractKeyListModel::~AbstractKeyListModel()
134void AbstractKeyListModel::setToolTipOptions(
int opts)
136 d->m_toolTipOptions = opts;
139int AbstractKeyListModel::toolTipOptions()
const
141 return d->m_toolTipOptions;
144void AbstractKeyListModel::setRemarkKeys(
const std::vector<GpgME::Key> &keys)
146 d->m_remarkKeys = keys;
149std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys()
const
151 return d->m_remarkKeys;
154Key AbstractKeyListModel::key(
const QModelIndex &idx)
const
158 key = doMapToKey(idx);
165 std::vector<Key> result;
166 result.reserve(indexes.
size());
167 std::transform(indexes.
begin(),
169 std::back_inserter(result),
171 return this->key(idx);
173 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
174 _detail::remove_duplicates_by_fpr(result);
178KeyGroup AbstractKeyListModel::group(
const QModelIndex &idx)
const
181 return doMapToGroup(idx);
187QModelIndex AbstractKeyListModel::index(
const Key &key)
const
189 return index(key, 0);
192QModelIndex AbstractKeyListModel::index(
const Key &key,
int col)
const
194 if (key.isNull() || col < 0 || col >= NumColumns) {
197 return doMapFromKey(key, col);
205 std::transform(keys.begin(),
207 std::back_inserter(result),
208 [
this](
const Key &key) {
209 return this->index(key);
214QModelIndex AbstractKeyListModel::index(
const KeyGroup &group)
const
216 return index(group, 0);
219QModelIndex AbstractKeyListModel::index(
const KeyGroup &group,
int col)
const
221 if (group.isNull() || col < 0 || col >= NumColumns) {
224 return doMapFromGroup(group, col);
228void AbstractKeyListModel::setKeys(
const std::vector<Key> &keys)
230 const bool inReset = modelResetInProgress();
241QModelIndex AbstractKeyListModel::addKey(
const Key &key)
243 const std::vector<Key> vec(1, key);
248void AbstractKeyListModel::removeKey(
const Key &key)
254 d->prettyEMailCache.remove(key.primaryFingerprint());
255 d->remarksCache.remove(key.primaryFingerprint());
260 std::vector<Key> sorted;
262 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull));
263 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
264 return doAddKeys(sorted);
267void AbstractKeyListModel::setGroups(
const std::vector<KeyGroup> &groups)
269 const bool inReset = modelResetInProgress();
280QModelIndex AbstractKeyListModel::addGroup(
const KeyGroup &group)
282 if (group.isNull()) {
285 return doAddGroup(group);
288bool AbstractKeyListModel::removeGroup(
const KeyGroup &group)
290 if (group.isNull()) {
293 return doRemoveGroup(group);
296void AbstractKeyListModel::clear(ItemTypes types)
298 const bool inReset = modelResetInProgress();
304 d->prettyEMailCache.clear();
305 d->remarksCache.clear();
312int AbstractKeyListModel::columnCount(
const QModelIndex &)
const
323 return i18nc(
"@title:column",
"Name");
325 return i18nc(
"@title:column",
"E-Mail");
327 return i18nc(
"@title:column",
"User-IDs");
329 return i18nc(
"@title:column",
"Valid From");
331 return i18nc(
"@title:column",
"Valid Until");
332 case TechnicalDetails:
333 return i18nc(
"@title:column",
"Protocol");
335 return i18nc(
"@title:column",
"Key-ID");
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.
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 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.shortKeyID());
438 return Formatting::prettyID(key.shortKeyID());
442 return Formatting::accessibleHexID(key.keyID());
444 return Formatting::prettyID(key.keyID());
447 return Formatting::summaryLine(key);
450 return Formatting::accessibleHexID(key.primaryFingerprint());
452 return Formatting::prettyID(key.primaryFingerprint());
457 return Formatting::origin(key.origin());
460 return Formatting::accessibleDate(key.lastUpdate());
462 return Formatting::dateString(key.lastUpdate());
467 return Formatting::ownerTrustShort(key.ownerTrust());
469 const char *
const fpr = key.primaryFingerprint();
470 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
471 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
472 return i18n(
"Loading...");
475 if (it != d->remarksCache.constEnd()) {
479 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
480 if (remarks.size() == 1) {
482 return d->remarksCache[fpr] = remark;
485 remarkList.
reserve(remarks.size());
486 for (
const auto &rem : remarks) {
489 const auto remark = remarkList.
join(QStringLiteral(
"; "));
490 return d->remarksCache[fpr] = remark;
499 return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
502 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
504 return Formatting::prettyID(key.subkey(0).keyGrip());
510 return Formatting::toolTip(key, toolTipOptions());
512 return KeyFilterManager::instance()->font(key,
513 (column == ShortKeyID || column == KeyID || column == Fingerprint) ?
QFont(QStringLiteral(
"monospace"))
516 return column ==
Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) :
QVariant();
518 if (!SystemInfo::isHighContrastModeActive()) {
519 return returnIfValid(KeyFilterManager::instance()->bgColor(key));
522 if (!SystemInfo::isHighContrastModeActive()) {
523 return returnIfValid(KeyFilterManager::instance()->fgColor(key));
525 }
else if (role == FingerprintRole) {
527 }
else if (role == KeyRole) {
533QVariant AbstractKeyListModel::data(
const KeyGroup &group,
int column,
int role)
const
540 return Formatting::complianceStringShort(group);
541 case TechnicalDetails:
542 return Formatting::type(group);
544 return Formatting::summaryLine(group);
558 return i18nc(
"text for screen readers",
"not applicable");
565 return Formatting::toolTip(group, toolTipOptions());
569 if (column !=
Icon && column != Summary) {
572 return Kleo::all_of(group.keys(),
573 [](
const auto &key) {
574 return key.hasEncrypt();
577 :
QIcon::fromTheme(QStringLiteral(
"emblem-warning"));
580 }
else if (role == GroupRole) {
586bool AbstractKeyListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
591 const KeyGroup group = value.
value<KeyGroup>();
592 return doSetGroupData(index, group);
598bool AbstractKeyListModel::modelResetInProgress()
600 return d->m_modelResetInProgress;
605template<
typename Base>
606class TableModelMixin :
public Base
609 explicit TableModelMixin(
QObject *p =
nullptr)
613 ~TableModelMixin()
override
620 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column,
nullptr) :
QModelIndex();
628 bool hasChildren(
const QModelIndex &pidx)
const override
630 return (pidx.
model() ==
this || !pidx.
isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
634class FlatKeyListModel
636 :
public TableModelMixin<AbstractKeyListModel>
638 :
public AbstractKeyListModel
643 explicit FlatKeyListModel(
QObject *parent =
nullptr);
644 ~FlatKeyListModel()
override;
646 int rowCount(
const QModelIndex &pidx)
const override
648 return pidx.
isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
652 Key doMapToKey(
const QModelIndex &index)
const override;
653 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
655 void doRemoveKey(
const Key &key)
override;
657 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
658 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
659 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
660 QModelIndex doAddGroup(
const KeyGroup &group)
override;
661 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
662 bool doRemoveGroup(
const KeyGroup &group)
override;
664 void doClear(ItemTypes types)
override
667 mKeysByFingerprint.clear();
669 if (types & Groups) {
674 int firstGroupRow()
const
676 return mKeysByFingerprint.size();
679 int lastGroupRow()
const
681 return mKeysByFingerprint.size() + mGroups.size() - 1;
686 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
689 return index.
row() - firstGroupRow();
693 std::vector<Key> mKeysByFingerprint;
694 std::vector<KeyGroup> mGroups;
697class HierarchicalKeyListModel :
public AbstractKeyListModel
701 explicit HierarchicalKeyListModel(
QObject *parent =
nullptr);
702 ~HierarchicalKeyListModel()
override;
704 int rowCount(
const QModelIndex &pidx)
const override;
705 using AbstractKeyListModel::index;
709 bool hasChildren(
const QModelIndex &pidx)
const override
711 return rowCount(pidx) > 0;
715 Key doMapToKey(
const QModelIndex &index)
const override;
716 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
718 void doRemoveKey(
const Key &key)
override;
720 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
721 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
722 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
723 QModelIndex doAddGroup(
const KeyGroup &group)
override;
724 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
725 bool doRemoveGroup(
const KeyGroup &group)
override;
727 void doClear(ItemTypes types)
override;
729 int firstGroupRow()
const
731 return mTopLevels.size();
734 int lastGroupRow()
const
736 return mTopLevels.size() + mGroups.size() - 1;
741 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
744 return index.
row() - firstGroupRow();
748 void addTopLevelKey(
const Key &key);
749 void addKeyWithParent(
const char *issuer_fpr,
const Key &key);
750 void addKeyWithoutParent(
const char *issuer_fpr,
const Key &key);
753 typedef std::map<std::string, std::vector<Key>>
Map;
754 std::vector<Key> mKeysByFingerprint;
755 Map mKeysByExistingParent, mKeysByNonExistingParent;
756 std::vector<Key> mTopLevels;
757 std::vector<KeyGroup> mGroups;
767 static Issuers *instance()
769 static auto self = std::unique_ptr<Issuers>{
new Issuers{}};
773 const char *cleanChainID(
const Key &key)
const
775 const char *chainID =
"";
777 const char *
const chid = key.chainID();
778 if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
785 void maskIssuerOfKey(
const Key &key)
787 mKeysWithMaskedIssuer.insert(key);
792 mKeysWithMaskedIssuer.clear();
796 std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
799static const char *cleanChainID(
const Key &key)
801 return Issuers::instance()->cleanChainID(key);
806FlatKeyListModel::FlatKeyListModel(
QObject *p)
807 : TableModelMixin<AbstractKeyListModel>(p)
811FlatKeyListModel::~FlatKeyListModel()
815Key FlatKeyListModel::doMapToKey(
const QModelIndex &idx)
const
818 if (
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() && idx.
column() < NumColumns) {
819 return mKeysByFingerprint[idx.
row()];
825QModelIndex FlatKeyListModel::doMapFromKey(
const Key &key,
int col)
const
827 Q_ASSERT(!key.isNull());
828 const std::vector<Key>::const_iterator it =
829 std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
830 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
833 return createIndex(it - mKeysByFingerprint.begin(), col);
839 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
845 for (
auto it = keys.begin(), end = keys.end(); it !=
end; ++it) {
847 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
848 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
850 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
852 mKeysByFingerprint[idx - 1] = *it;
853 if (!modelResetInProgress()) {
854 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
858 if (!modelResetInProgress()) {
861 mKeysByFingerprint.insert(pos, *it);
862 if (!modelResetInProgress()) {
868 return indexes(keys);
871void FlatKeyListModel::doRemoveKey(
const Key &key)
873 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
874 if (it == mKeysByFingerprint.end()) {
878 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
879 if (!modelResetInProgress()) {
882 mKeysByFingerprint.erase(it);
883 if (!modelResetInProgress()) {
888KeyGroup FlatKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
891 if (
static_cast<unsigned>(idx.
row()) >= mKeysByFingerprint.size() &&
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() + mGroups.size()
892 && idx.
column() < NumColumns) {
893 return mGroups[idx.
row() - mKeysByFingerprint.size()];
899QModelIndex FlatKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
901 Q_ASSERT(!group.isNull());
902 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
903 return g.source() == group.source() && g.id() == group.id();
905 if (it == mGroups.cend()) {
908 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
912void FlatKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
914 Q_ASSERT(mGroups.empty());
915 const int first = mKeysByFingerprint.size();
916 const int last = first + groups.size() - 1;
917 if (!modelResetInProgress()) {
921 if (!modelResetInProgress()) {
926QModelIndex FlatKeyListModel::doAddGroup(
const KeyGroup &group)
928 const int newRow = lastGroupRow() + 1;
929 if (!modelResetInProgress()) {
932 mGroups.push_back(group);
933 if (!modelResetInProgress()) {
936 return createIndex(newRow, 0);
939bool FlatKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
941 if (group.isNull()) {
944 const int groupIndex = this->groupIndex(index);
945 if (groupIndex == -1) {
948 mGroups[groupIndex] = group;
949 if (!modelResetInProgress()) {
950 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
955bool FlatKeyListModel::doRemoveGroup(
const KeyGroup &group)
957 const QModelIndex modelIndex = doMapFromGroup(group, 0);
961 const int groupIndex = this->groupIndex(modelIndex);
962 Q_ASSERT(groupIndex != -1);
963 if (groupIndex == -1) {
966 if (!modelResetInProgress()) {
969 mGroups.erase(mGroups.begin() + groupIndex);
970 if (!modelResetInProgress()) {
976HierarchicalKeyListModel::HierarchicalKeyListModel(
QObject *p)
977 : AbstractKeyListModel(p)
978 , mKeysByFingerprint()
979 , mKeysByExistingParent()
980 , mKeysByNonExistingParent()
985HierarchicalKeyListModel::~HierarchicalKeyListModel()
989int HierarchicalKeyListModel::rowCount(
const QModelIndex &pidx)
const
993 return mTopLevels.size() + mGroups.size();
1001 const Key issuer = this->key(pidx);
1002 const char *
const fpr = issuer.primaryFingerprint();
1003 if (!fpr || !*fpr) {
1006 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1007 if (it == mKeysByExistingParent.end()) {
1010 return it->second.size();
1015 if (row < 0 || col < 0 || col >= NumColumns) {
1021 if (
static_cast<unsigned>(row) < mTopLevels.size()) {
1022 return index(mTopLevels[row], col);
1023 }
else if (
static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1024 return index(mGroups[row - mTopLevels.size()], col);
1031 const Key issuer = this->key(pidx);
1032 const char *
const fpr = issuer.primaryFingerprint();
1033 if (!fpr || !*fpr) {
1036 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1037 if (it == mKeysByExistingParent.end() ||
static_cast<unsigned>(row) >= it->second.size()) {
1040 return index(it->second[row], col);
1045 const Key key = this->key(idx);
1046 if (key.isNull() || key.isRoot()) {
1049 const std::vector<Key>::const_iterator it =
1050 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1051 return it != mKeysByFingerprint.end() ? index(*it) :
QModelIndex();
1054Key HierarchicalKeyListModel::doMapToKey(
const QModelIndex &idx)
const
1056 Key key = Key::null;
1059 const char *
const issuer_fpr =
static_cast<const char *
>(idx.
internalPointer());
1060 if (!issuer_fpr || !*issuer_fpr) {
1062 if (
static_cast<unsigned>(idx.
row()) < mTopLevels.size()) {
1063 key = mTopLevels[idx.
row()];
1067 const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1068 if (it != mKeysByExistingParent.end() &&
static_cast<unsigned>(idx.
row()) < it->second.size()) {
1069 key = it->second[idx.
row()];
1077QModelIndex HierarchicalKeyListModel::doMapFromKey(
const Key &key,
int col)
const
1083 const char *issuer_fpr = cleanChainID(key);
1086 const std::vector<Key> *v = &mTopLevels;
1087 if (issuer_fpr && *issuer_fpr) {
1088 const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1090 if (it != mKeysByExistingParent.end()) {
1093 issuer_fpr =
nullptr;
1097 const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1098 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1102 const unsigned int row = std::distance(v->begin(), it);
1103 return createIndex(row, col,
const_cast<char *
>(issuer_fpr));
1106void HierarchicalKeyListModel::addKeyWithParent(
const char *issuer_fpr,
const Key &key)
1108 Q_ASSERT(issuer_fpr);
1109 Q_ASSERT(*issuer_fpr);
1110 Q_ASSERT(!key.isNull());
1112 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1115 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1116 const int row = std::distance(subjects.begin(), it);
1118 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1121 if (!modelResetInProgress()) {
1122 Q_EMIT dataChanged(createIndex(row, 0,
const_cast<char *
>(issuer_fpr)), createIndex(row, NumColumns - 1,
const_cast<char *
>(issuer_fpr)));
1126 const std::vector<Key>::const_iterator pos =
1127 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1128 Q_ASSERT(pos != mKeysByFingerprint.end());
1129 if (!modelResetInProgress()) {
1130 beginInsertRows(index(*pos), row, row);
1132 subjects.insert(it, key);
1133 if (!modelResetInProgress()) {
1139void HierarchicalKeyListModel::addKeyWithoutParent(
const char *issuer_fpr,
const Key &key)
1141 Q_ASSERT(issuer_fpr);
1142 Q_ASSERT(*issuer_fpr);
1143 Q_ASSERT(!key.isNull());
1145 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1148 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1150 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1155 subjects.insert(it, key);
1158 addTopLevelKey(key);
1161void HierarchicalKeyListModel::addTopLevelKey(
const Key &key)
1164 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1165 const int row = std::distance(mTopLevels.begin(), it);
1167 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1170 if (!modelResetInProgress()) {
1171 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1175 if (!modelResetInProgress()) {
1178 mTopLevels.insert(it, key);
1179 if (!modelResetInProgress()) {
1189struct cycle_detector :
public boost::dfs_visitor<> {
1190 cycle_detector(
bool &has_cycle)
1191 : _has_cycle{has_cycle}
1195 template<
class Edge,
class Graph>
1196 void back_edge(Edge, Graph &)
1205static bool graph_has_cycle(
const boost::adjacency_list<> &graph)
1207 bool cycle_found =
false;
1208 cycle_detector vis{cycle_found};
1209 boost::depth_first_search(graph, visitor(vis));
1213static void find_keys_causing_cycles_and_mask_their_issuers(
const std::vector<Key> &keys)
1215 boost::adjacency_list<> graph{keys.size()};
1217 for (
unsigned int i = 0, end = keys.size(); i !=
end; ++i) {
1218 const auto &key = keys[i];
1219 const char *
const issuer_fpr = cleanChainID(key);
1220 if (!issuer_fpr || !*issuer_fpr) {
1223 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1224 if (it == keys.end()) {
1227 const auto j = std::distance(keys.begin(), it);
1228 const auto edge = boost::add_edge(i, j, graph).first;
1229 if (graph_has_cycle(graph)) {
1230 Issuers::instance()->maskIssuerOfKey(key);
1231 boost::remove_edge(edge, graph);
1236static auto build_key_graph(
const std::vector<Key> &keys)
1238 boost::adjacency_list<> graph(keys.size());
1241 for (
unsigned int i = 0, end = keys.size(); i !=
end; ++i) {
1242 const char *
const issuer_fpr = cleanChainID(keys[i]);
1243 if (!issuer_fpr || !*issuer_fpr) {
1246 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1247 if (it == keys.end()) {
1250 const auto j = std::distance(keys.begin(), it);
1251 add_edge(i, j, graph);
1258static std::vector<Key> topological_sort(
const std::vector<Key> &keys)
1260 const auto graph = build_key_graph(keys);
1262 std::vector<int> order;
1263 order.reserve(keys.size());
1264 topological_sort(graph, std::back_inserter(order));
1266 Q_ASSERT(order.size() == keys.size());
1268 std::vector<Key> result;
1270 for (
int i :
std::as_const(order)) {
1271 result.push_back(keys[i]);
1280 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1286 const std::vector<Key> oldKeys = mKeysByFingerprint;
1288 std::vector<Key> merged;
1289 merged.reserve(keys.size() + mKeysByFingerprint.size());
1290 std::set_union(keys.begin(),
1292 mKeysByFingerprint.begin(),
1293 mKeysByFingerprint.end(),
1294 std::back_inserter(merged),
1295 _detail::ByFingerprint<std::less>());
1297 mKeysByFingerprint = merged;
1299 if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1300 find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1303 std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1305 const auto topologicalSortedList = topological_sort(keys);
1306 for (
const Key &key : topologicalSortedList) {
1308 const char *
const fpr = key.primaryFingerprint();
1309 if (!fpr || !*fpr) {
1313 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1315 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1316 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1317 if (it != mKeysByNonExistingParent.end()) {
1318 mKeysByNonExistingParent.erase(it);
1323 if (!keyAlreadyExisted) {
1324 auto last = mTopLevels.begin();
1325 auto lastFP = mKeysByFingerprint.begin();
1327 for (
const Key &k : children) {
1328 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1329 Q_ASSERT(last != mTopLevels.end());
1330 const int row = std::distance(mTopLevels.begin(), last);
1332 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1333 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1336 if (!modelResetInProgress()) {
1339 last = mTopLevels.erase(last);
1340 lastFP = mKeysByFingerprint.erase(lastFP);
1341 if (!modelResetInProgress()) {
1348 const char *
const issuer_fpr = cleanChainID(key);
1349 if (!issuer_fpr || !*issuer_fpr) {
1351 addTopLevelKey(key);
1352 }
else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1354 addKeyWithParent(issuer_fpr, key);
1357 addKeyWithoutParent(issuer_fpr, key);
1362 while (key_parent.
isValid()) {
1363 changedParents.insert(doMapToKey(key_parent));
1364 key_parent = key_parent.
parent();
1369 if (!keyAlreadyExisted && !children.empty()) {
1374 for (
int i = children.size() - 1; i >= 0; --i) {
1375 Q_EMIT rowMoved(new_parent, i);
1381 if (!modelResetInProgress()) {
1382 for (
const Key &i :
std::as_const(changedParents)) {
1389 return indexes(keys);
1392void HierarchicalKeyListModel::doRemoveKey(
const Key &key)
1399 const char *
const fpr = key.primaryFingerprint();
1400 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1402 std::vector<Key> keys = mKeysByFingerprint;
1403 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1404 if (it == keys.end()) {
1417 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1419 Q_ASSERT(it != mKeysByFingerprint.end());
1420 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1421 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1423 if (!modelResetInProgress()) {
1424 beginRemoveRows(parent(idx), idx.
row(), idx.
row());
1426 mKeysByFingerprint.erase(it);
1428 const char *
const issuer_fpr = cleanChainID(key);
1430 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1431 if (tlIt != mTopLevels.end()) {
1432 mTopLevels.erase(tlIt);
1435 if (issuer_fpr && *issuer_fpr) {
1436 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1437 if (nexIt != mKeysByNonExistingParent.end()) {
1438 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1439 if (eit != nexIt->second.end()) {
1440 nexIt->second.erase(eit);
1442 if (nexIt->second.empty()) {
1443 mKeysByNonExistingParent.erase(nexIt);
1447 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1448 if (exIt != mKeysByExistingParent.end()) {
1449 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1450 if (eit != exIt->second.end()) {
1451 exIt->second.erase(eit);
1453 if (exIt->second.empty()) {
1454 mKeysByExistingParent.erase(exIt);
1458 if (!modelResetInProgress()) {
1463KeyGroup HierarchicalKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
1471 if (
static_cast<unsigned>(idx.
row()) >= mTopLevels.size() &&
static_cast<unsigned>(idx.
row()) < mTopLevels.size() + mGroups.size()
1472 && idx.
column() < NumColumns) {
1473 return mGroups[idx.
row() - mTopLevels.size()];
1479QModelIndex HierarchicalKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
1481 Q_ASSERT(!group.isNull());
1482 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
1483 return g.source() == group.source() && g.id() == group.id();
1485 if (it == mGroups.cend()) {
1488 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1492void HierarchicalKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
1494 Q_ASSERT(mGroups.empty());
1495 const int first = mTopLevels.size();
1496 const int last = first + groups.size() - 1;
1497 if (!modelResetInProgress()) {
1501 if (!modelResetInProgress()) {
1506QModelIndex HierarchicalKeyListModel::doAddGroup(
const KeyGroup &group)
1508 const int newRow = lastGroupRow() + 1;
1509 if (!modelResetInProgress()) {
1512 mGroups.push_back(group);
1513 if (!modelResetInProgress()) {
1516 return createIndex(newRow, 0);
1519bool HierarchicalKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
1521 if (group.isNull()) {
1524 const int groupIndex = this->groupIndex(index);
1525 if (groupIndex == -1) {
1528 mGroups[groupIndex] = group;
1529 if (!modelResetInProgress()) {
1530 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
1535bool HierarchicalKeyListModel::doRemoveGroup(
const KeyGroup &group)
1537 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1541 const int groupIndex = this->groupIndex(modelIndex);
1542 Q_ASSERT(groupIndex != -1);
1543 if (groupIndex == -1) {
1546 if (!modelResetInProgress()) {
1549 mGroups.erase(mGroups.begin() + groupIndex);
1550 if (!modelResetInProgress()) {
1556void HierarchicalKeyListModel::doClear(ItemTypes types)
1560 mKeysByFingerprint.clear();
1561 mKeysByExistingParent.clear();
1562 mKeysByNonExistingParent.clear();
1563 Issuers::instance()->clear();
1565 if (types & Groups) {
1570void AbstractKeyListModel::useKeyCache(
bool value, KeyList::Options options)
1572 d->m_keyListOptions = options;
1573 d->m_useKeyCache = value;
1574 if (!d->m_useKeyCache) {
1577 d->updateFromKeyCache();
1579 connect(KeyCache::instance().
get(), &KeyCache::keysMayHaveChanged,
this, [
this] {
1580 d->updateFromKeyCache();
1585AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(
QObject *p)
1587 AbstractKeyListModel *
const m =
new FlatKeyListModel(p);
1588#ifdef KLEO_MODEL_TEST
1595AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(
QObject *p)
1597 AbstractKeyListModel *
const m =
new HierarchicalKeyListModel(p);
1598#ifdef KLEO_MODEL_TEST
1604QMimeData *AbstractKeyListModel::mimeData(
const QModelIndexList &indexes)
const
1606 if (d->m_dragHandler) {
1607 return d->m_dragHandler->mimeData(indexes);
1615 if (d->m_dragHandler) {
1616 return d->m_dragHandler->flags(index);
1622QStringList AbstractKeyListModel::mimeTypes()
const
1624 if (d->m_dragHandler) {
1625 return d->m_dragHandler->mimeTypes();
1631void AbstractKeyListModel::setDragHandler(
const std::shared_ptr<DragHandler> &dragHandler)
1633 d->m_dragHandler = dragHandler;
1636#include "keylistmodel.moc"
1675#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)
const QList< QKeySequence > & end()
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)