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/keyhelpers.h>
23#include <libkleo/predicates.h>
24#include <libkleo/systeminfo.h>
26#include <KLocalizedString>
29#include <QAbstractItemModelTester>
38#include <gpgme++/key.h>
41#include <boost/graph/adjacency_list.hpp>
42#include <boost/graph/topological_sort.hpp>
52using namespace Kleo::KeyList;
55Q_DECLARE_METATYPE(GpgME::Key)
56Q_DECLARE_METATYPE(KeyGroup)
59class AbstractKeyListModel::Private
61 AbstractKeyListModel *
const q;
64 explicit Private(AbstractKeyListModel *qq);
66 void updateFromKeyCache();
68 QString getEMail(
const Key &key)
const;
71 int m_toolTipOptions = Formatting::Validity;
74 bool m_useKeyCache =
false;
75 bool m_modelResetInProgress =
false;
76 KeyList::Options m_keyListOptions = AllKeys;
77 std::vector<GpgME::Key> m_remarkKeys;
78 std::shared_ptr<DragHandler> m_dragHandler;
79 std::vector<Key::Origin> extraOrigins;
82AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
87void AbstractKeyListModel::Private::updateFromKeyCache()
90 const bool inReset = q->modelResetInProgress();
94 q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
95 if (m_keyListOptions == IncludeGroups) {
96 q->setGroups(KeyCache::instance()->groups());
104QString AbstractKeyListModel::Private::getEMail(
const Key &key)
const
107 if (
const auto fpr = key.primaryFingerprint()) {
108 const auto it = prettyEMailCache.constFind(fpr);
109 if (it != prettyEMailCache.constEnd()) {
112 email = Formatting::prettyEMail(key);
113 prettyEMailCache[fpr] = email;
119AbstractKeyListModel::AbstractKeyListModel(
QObject *p)
121 , KeyListModelInterface()
122 , d(new Private(this))
125 d->m_modelResetInProgress =
true;
128 d->m_modelResetInProgress =
false;
132AbstractKeyListModel::~AbstractKeyListModel()
136void AbstractKeyListModel::setToolTipOptions(
int opts)
138 d->m_toolTipOptions = opts;
141int AbstractKeyListModel::toolTipOptions()
const
143 return d->m_toolTipOptions;
146void AbstractKeyListModel::setRemarkKeys(
const std::vector<GpgME::Key> &keys)
148 d->m_remarkKeys = keys;
151std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys()
const
153 return d->m_remarkKeys;
156Key AbstractKeyListModel::key(
const QModelIndex &idx)
const
160 key = doMapToKey(idx);
167 std::vector<Key> result;
168 result.reserve(indexes.
size());
169 std::transform(indexes.
begin(),
171 std::back_inserter(result),
173 return this->key(idx);
175 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
176 _detail::remove_duplicates_by_fpr(result);
180KeyGroup AbstractKeyListModel::group(
const QModelIndex &idx)
const
183 return doMapToGroup(idx);
189QModelIndex AbstractKeyListModel::index(
const Key &key)
const
191 return index(key, 0);
194QModelIndex AbstractKeyListModel::index(
const Key &key,
int col)
const
196 if (key.isNull() || col < 0 || col >= NumColumns) {
199 return doMapFromKey(key, col);
207 std::transform(keys.begin(),
209 std::back_inserter(result),
210 [
this](
const Key &key) {
211 return this->index(key);
216QModelIndex AbstractKeyListModel::index(
const KeyGroup &group)
const
218 return index(group, 0);
221QModelIndex AbstractKeyListModel::index(
const KeyGroup &group,
int col)
const
223 if (group.isNull() || col < 0 || col >= NumColumns) {
226 return doMapFromGroup(group, col);
230void AbstractKeyListModel::setKeys(
const std::vector<Key> &keys,
const std::vector<Key::Origin> &extraOrigins)
232 const bool inReset = modelResetInProgress();
238 d->extraOrigins = extraOrigins;
244QModelIndex AbstractKeyListModel::addKey(
const Key &key)
246 const std::vector<Key> vec(1, key);
251void AbstractKeyListModel::removeKey(
const Key &key)
257 d->prettyEMailCache.remove(key.primaryFingerprint());
258 d->remarksCache.remove(key.primaryFingerprint());
263 std::vector<Key> sorted;
265 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull));
266 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
267 return doAddKeys(sorted);
270void AbstractKeyListModel::setGroups(
const std::vector<KeyGroup> &groups)
272 const bool inReset = modelResetInProgress();
283QModelIndex AbstractKeyListModel::addGroup(
const KeyGroup &group)
285 if (group.isNull()) {
288 return doAddGroup(group);
291bool AbstractKeyListModel::removeGroup(
const KeyGroup &group)
293 if (group.isNull()) {
296 return doRemoveGroup(group);
299void AbstractKeyListModel::clear(ItemTypes types)
301 const bool inReset = modelResetInProgress();
307 d->prettyEMailCache.clear();
308 d->remarksCache.clear();
315int AbstractKeyListModel::columnCount(
const QModelIndex &)
const
326 return i18nc(
"@title:column",
"Name");
328 return i18nc(
"@title:column",
"E-Mail");
330 return i18nc(
"@title:column",
"Status");
332 return i18nc(
"@title:column",
"Valid From");
334 return i18nc(
"@title:column",
"Valid Until");
335 case TechnicalDetails:
336 return i18nc(
"@title:column",
"Protocol");
338 return i18nc(
"@title:column",
"Key ID");
340 return i18nc(
"@title:column",
"Fingerprint");
342 return i18nc(
"@title:column",
"Issuer");
344 return i18nc(
"@title:column",
"Serial Number");
346 return i18nc(
"@title:column",
"Origin");
348 return i18nc(
"@title:column",
"Last Update");
350 return i18nc(
"@title:column",
"Certification Trust");
352 return i18nc(
"@title:column",
"Tags");
354 return i18nc(
"@title:column",
"Algorithm");
356 return i18nc(
"@title:column",
"Keygrip");
384 const Key key = this->key(index);
386 return data(key, index.
row(), index.
column(), role);
389 const KeyGroup group = this->group(index);
390 if (!group.isNull()) {
391 return data(group, index.
column(), role);
397QVariant AbstractKeyListModel::data(
const Key &key,
int row,
int column,
int role)
const
402 const auto name = Formatting::prettyName(key);
404 return name.isEmpty() ?
i18nc(
"text for screen readers for an empty name",
"no name") : name;
409 const auto email = d->getEMail(key);
411 return email.
isEmpty() ?
i18nc(
"text for screen readers for an empty email address",
"no email") : email;
416 return Formatting::complianceStringShort(key);
419 return Formatting::creationDate(key);
421 return Formatting::accessibleCreationDate(key);
423 return Formatting::creationDateString(key);
427 return Formatting::expirationDate(key);
429 return Formatting::accessibleExpirationDate(key);
431 return Formatting::expirationDateString(key);
433 case TechnicalDetails:
434 return Formatting::type(key);
437 return Formatting::accessibleHexID(key.keyID());
438 }
else if (role == ClipboardRole) {
441 return Formatting::prettyID(key.keyID());
444 return Formatting::summaryLine(key);
447 return Formatting::accessibleHexID(key.primaryFingerprint());
448 }
else if (role == ClipboardRole) {
451 return Formatting::prettyID(key.primaryFingerprint());
456 if (key.origin() == Key::OriginUnknown && (
int)d->extraOrigins.size() > row) {
457 return Formatting::origin(d->extraOrigins[row]);
459 return Formatting::origin(key.origin());
462 return Formatting::accessibleDate(key.lastUpdate());
464 return Formatting::dateString(key.lastUpdate());
469 return Formatting::ownerTrustShort(key.ownerTrust());
471 const char *
const fpr = key.primaryFingerprint();
472 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
473 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
474 return i18n(
"Loading...");
477 if (it != d->remarksCache.constEnd()) {
481 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
482 if (remarks.size() == 1) {
484 return d->remarksCache[fpr] = remark;
487 remarkList.
reserve(remarks.size());
488 for (
const auto &rem : remarks) {
491 const auto remark = remarkList.
join(QStringLiteral(
"; "));
492 return d->remarksCache[fpr] = remark;
501 return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
504 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
512 return Formatting::toolTip(key, toolTipOptions());
514 return KeyFilterManager::instance()->font(key, (column == KeyID || column == Fingerprint) ?
QFont(QStringLiteral(
"monospace")) :
QFont());
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);
557 return i18nc(
"text for screen readers",
"not applicable");
564 return Formatting::toolTip(group, toolTipOptions());
568 if (column !=
Icon && column != Summary) {
571 return Kleo::all_of(group.keys(),
572 [](
const auto &key) {
573 return Kleo::canBeUsedForEncryption(key);
576 :
QIcon::fromTheme(QStringLiteral(
"emblem-warning"));
579 }
else if (role == GroupRole) {
585bool AbstractKeyListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
590 const KeyGroup group = value.
value<KeyGroup>();
591 return doSetGroupData(index, group);
597bool AbstractKeyListModel::modelResetInProgress()
599 return d->m_modelResetInProgress;
604template<
typename Base>
605class TableModelMixin :
public Base
608 explicit TableModelMixin(
QObject *p =
nullptr)
612 ~TableModelMixin()
override
619 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column,
nullptr) :
QModelIndex();
627 bool hasChildren(
const QModelIndex &pidx)
const override
629 return (pidx.
model() ==
this || !pidx.
isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
633class FlatKeyListModel
635 :
public TableModelMixin<AbstractKeyListModel>
637 :
public AbstractKeyListModel
642 explicit FlatKeyListModel(
QObject *parent =
nullptr);
643 ~FlatKeyListModel()
override;
645 int rowCount(
const QModelIndex &pidx)
const override
647 return pidx.
isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
651 Key doMapToKey(
const QModelIndex &index)
const override;
652 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
654 void doRemoveKey(
const Key &key)
override;
656 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
657 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
658 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
659 QModelIndex doAddGroup(
const KeyGroup &group)
override;
660 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
661 bool doRemoveGroup(
const KeyGroup &group)
override;
663 void doClear(ItemTypes types)
override
666 mKeysByFingerprint.clear();
668 if (types & Groups) {
673 int firstGroupRow()
const
675 return mKeysByFingerprint.size();
678 int lastGroupRow()
const
680 return mKeysByFingerprint.size() + mGroups.size() - 1;
685 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
688 return index.
row() - firstGroupRow();
692 std::vector<Key> mKeysByFingerprint;
693 std::vector<KeyGroup> mGroups;
696class HierarchicalKeyListModel :
public AbstractKeyListModel
700 explicit HierarchicalKeyListModel(
QObject *parent =
nullptr);
701 ~HierarchicalKeyListModel()
override;
703 int rowCount(
const QModelIndex &pidx)
const override;
704 using AbstractKeyListModel::index;
708 bool hasChildren(
const QModelIndex &pidx)
const override
710 return rowCount(pidx) > 0;
714 Key doMapToKey(
const QModelIndex &index)
const override;
715 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
717 void doRemoveKey(
const Key &key)
override;
719 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
720 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
721 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
722 QModelIndex doAddGroup(
const KeyGroup &group)
override;
723 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
724 bool doRemoveGroup(
const KeyGroup &group)
override;
726 void doClear(ItemTypes types)
override;
728 int firstGroupRow()
const
730 return mTopLevels.size();
733 int lastGroupRow()
const
735 return mTopLevels.size() + mGroups.size() - 1;
740 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
743 return index.
row() - firstGroupRow();
747 void addTopLevelKey(
const Key &key);
748 void addKeyWithParent(
const char *issuer_fpr,
const Key &key);
749 void addKeyWithoutParent(
const char *issuer_fpr,
const Key &key);
752 typedef std::map<std::string, std::vector<Key>>
Map;
753 std::vector<Key> mKeysByFingerprint;
754 Map mKeysByExistingParent, mKeysByNonExistingParent;
755 std::vector<Key> mTopLevels;
756 std::vector<KeyGroup> mGroups;
766 static Issuers *instance()
768 static auto self = std::unique_ptr<Issuers>{
new Issuers{}};
772 const char *cleanChainID(
const Key &key)
const
774 const char *chainID =
"";
776 const char *
const chid = key.chainID();
777 if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
784 void maskIssuerOfKey(
const Key &key)
786 mKeysWithMaskedIssuer.insert(key);
791 mKeysWithMaskedIssuer.clear();
795 std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
798static const char *cleanChainID(
const Key &key)
800 return Issuers::instance()->cleanChainID(key);
805FlatKeyListModel::FlatKeyListModel(
QObject *p)
806 : TableModelMixin<AbstractKeyListModel>(p)
810FlatKeyListModel::~FlatKeyListModel()
814Key FlatKeyListModel::doMapToKey(
const QModelIndex &idx)
const
817 if (
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() && idx.
column() < NumColumns) {
818 return mKeysByFingerprint[idx.
row()];
824QModelIndex FlatKeyListModel::doMapFromKey(
const Key &key,
int col)
const
826 Q_ASSERT(!key.isNull());
827 const std::vector<Key>::const_iterator it =
828 std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
829 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
832 return createIndex(it - mKeysByFingerprint.begin(), col);
838 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
844 for (
auto it = keys.begin(), end = keys.end(); it != end; ++it) {
846 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
847 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
849 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
851 mKeysByFingerprint[idx - 1] = *it;
852 if (!modelResetInProgress()) {
853 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
857 if (!modelResetInProgress()) {
860 mKeysByFingerprint.insert(pos, *it);
861 if (!modelResetInProgress()) {
867 return indexes(keys);
870void FlatKeyListModel::doRemoveKey(
const Key &key)
872 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
873 if (it == mKeysByFingerprint.end()) {
877 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
878 if (!modelResetInProgress()) {
881 mKeysByFingerprint.erase(it);
882 if (!modelResetInProgress()) {
887KeyGroup FlatKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
890 if (
static_cast<unsigned>(idx.
row()) >= mKeysByFingerprint.size() &&
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() + mGroups.size()
891 && idx.
column() < NumColumns) {
892 return mGroups[idx.
row() - mKeysByFingerprint.size()];
898QModelIndex FlatKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
900 Q_ASSERT(!group.isNull());
901 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
902 return g.source() == group.source() && g.id() == group.id();
904 if (it == mGroups.cend()) {
907 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
911void FlatKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
913 Q_ASSERT(mGroups.empty());
914 const int first = mKeysByFingerprint.size();
915 const int last = first + groups.size() - 1;
916 if (!modelResetInProgress()) {
920 if (!modelResetInProgress()) {
925QModelIndex FlatKeyListModel::doAddGroup(
const KeyGroup &group)
927 const int newRow = lastGroupRow() + 1;
928 if (!modelResetInProgress()) {
931 mGroups.push_back(group);
932 if (!modelResetInProgress()) {
935 return createIndex(newRow, 0);
938bool FlatKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
940 if (group.isNull()) {
943 const int groupIndex = this->groupIndex(index);
944 if (groupIndex == -1) {
947 mGroups[groupIndex] = group;
948 if (!modelResetInProgress()) {
949 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
954bool FlatKeyListModel::doRemoveGroup(
const KeyGroup &group)
956 const QModelIndex modelIndex = doMapFromGroup(group, 0);
960 const int groupIndex = this->groupIndex(modelIndex);
961 Q_ASSERT(groupIndex != -1);
962 if (groupIndex == -1) {
965 if (!modelResetInProgress()) {
968 mGroups.erase(mGroups.begin() + groupIndex);
969 if (!modelResetInProgress()) {
975HierarchicalKeyListModel::HierarchicalKeyListModel(
QObject *p)
976 : AbstractKeyListModel(p)
977 , mKeysByFingerprint()
978 , mKeysByExistingParent()
979 , mKeysByNonExistingParent()
984HierarchicalKeyListModel::~HierarchicalKeyListModel()
988int HierarchicalKeyListModel::rowCount(
const QModelIndex &pidx)
const
992 return mTopLevels.size() + mGroups.size();
1000 const Key issuer = this->key(pidx);
1001 const char *
const fpr = issuer.primaryFingerprint();
1002 if (!fpr || !*fpr) {
1005 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1006 if (it == mKeysByExistingParent.end()) {
1009 return it->second.size();
1014 if (row < 0 || col < 0 || col >= NumColumns) {
1020 if (
static_cast<unsigned>(row) < mTopLevels.size()) {
1021 return index(mTopLevels[row], col);
1022 }
else if (
static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1023 return index(mGroups[row - mTopLevels.size()], col);
1030 const Key issuer = this->key(pidx);
1031 const char *
const fpr = issuer.primaryFingerprint();
1032 if (!fpr || !*fpr) {
1035 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1036 if (it == mKeysByExistingParent.end() ||
static_cast<unsigned>(row) >= it->second.size()) {
1039 return index(it->second[row], col);
1044 const Key key = this->key(idx);
1045 if (key.isNull() || key.isRoot()) {
1048 const std::vector<Key>::const_iterator it =
1049 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1050 return it != mKeysByFingerprint.end() ? index(*it) :
QModelIndex();
1053Key HierarchicalKeyListModel::doMapToKey(
const QModelIndex &idx)
const
1055 Key key = Key::null;
1058 const char *
const issuer_fpr =
static_cast<const char *
>(idx.
internalPointer());
1059 if (!issuer_fpr || !*issuer_fpr) {
1061 if (
static_cast<unsigned>(idx.
row()) < mTopLevels.size()) {
1062 key = mTopLevels[idx.
row()];
1066 const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1067 if (it != mKeysByExistingParent.end() &&
static_cast<unsigned>(idx.
row()) < it->second.size()) {
1068 key = it->second[idx.
row()];
1076QModelIndex HierarchicalKeyListModel::doMapFromKey(
const Key &key,
int col)
const
1082 const char *issuer_fpr = cleanChainID(key);
1085 const std::vector<Key> *v = &mTopLevels;
1086 if (issuer_fpr && *issuer_fpr) {
1087 const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1089 if (it != mKeysByExistingParent.end()) {
1092 issuer_fpr =
nullptr;
1096 const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1097 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1101 const unsigned int row = std::distance(v->begin(), it);
1102 return createIndex(row, col,
const_cast<char *
>(issuer_fpr));
1105void HierarchicalKeyListModel::addKeyWithParent(
const char *issuer_fpr,
const Key &key)
1107 Q_ASSERT(issuer_fpr);
1108 Q_ASSERT(*issuer_fpr);
1109 Q_ASSERT(!key.isNull());
1111 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1114 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1115 const int row = std::distance(subjects.begin(), it);
1117 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1120 if (!modelResetInProgress()) {
1121 Q_EMIT dataChanged(createIndex(row, 0,
const_cast<char *
>(issuer_fpr)), createIndex(row, NumColumns - 1,
const_cast<char *
>(issuer_fpr)));
1125 const std::vector<Key>::const_iterator pos =
1126 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1127 Q_ASSERT(pos != mKeysByFingerprint.end());
1128 if (!modelResetInProgress()) {
1129 beginInsertRows(index(*pos), row, row);
1131 subjects.insert(it, key);
1132 if (!modelResetInProgress()) {
1138void HierarchicalKeyListModel::addKeyWithoutParent(
const char *issuer_fpr,
const Key &key)
1140 Q_ASSERT(issuer_fpr);
1141 Q_ASSERT(*issuer_fpr);
1142 Q_ASSERT(!key.isNull());
1144 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1147 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1149 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1154 subjects.insert(it, key);
1157 addTopLevelKey(key);
1160void HierarchicalKeyListModel::addTopLevelKey(
const Key &key)
1163 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1164 const int row = std::distance(mTopLevels.begin(), it);
1166 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1169 if (!modelResetInProgress()) {
1170 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1174 if (!modelResetInProgress()) {
1177 mTopLevels.insert(it, key);
1178 if (!modelResetInProgress()) {
1188struct cycle_detector :
public boost::dfs_visitor<> {
1189 cycle_detector(
bool &has_cycle)
1190 : _has_cycle{has_cycle}
1194 template<
class Edge,
class Graph>
1195 void back_edge(Edge, Graph &)
1204static bool graph_has_cycle(
const boost::adjacency_list<> &graph)
1206 bool cycle_found =
false;
1207 cycle_detector vis{cycle_found};
1208 boost::depth_first_search(graph, visitor(vis));
1212static void find_keys_causing_cycles_and_mask_their_issuers(
const std::vector<Key> &keys)
1214 boost::adjacency_list<> graph{keys.size()};
1216 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1217 const auto &key = keys[i];
1218 const char *
const issuer_fpr = cleanChainID(key);
1219 if (!issuer_fpr || !*issuer_fpr) {
1222 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1223 if (it == keys.end()) {
1226 const auto j = std::distance(keys.begin(), it);
1227 const auto edge = boost::add_edge(i, j, graph).first;
1228 if (graph_has_cycle(graph)) {
1229 Issuers::instance()->maskIssuerOfKey(key);
1230 boost::remove_edge(edge, graph);
1235static auto build_key_graph(
const std::vector<Key> &keys)
1237 boost::adjacency_list<> graph(keys.size());
1240 for (
unsigned int i = 0, end = keys.size(); i != end; ++i) {
1241 const char *
const issuer_fpr = cleanChainID(keys[i]);
1242 if (!issuer_fpr || !*issuer_fpr) {
1245 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1246 if (it == keys.end()) {
1249 const auto j = std::distance(keys.begin(), it);
1250 add_edge(i, j, graph);
1257static std::vector<Key> topological_sort(
const std::vector<Key> &keys)
1259 const auto graph = build_key_graph(keys);
1261 std::vector<int> order;
1262 order.reserve(keys.size());
1263 topological_sort(graph, std::back_inserter(order));
1265 Q_ASSERT(order.size() == keys.size());
1267 std::vector<Key> result;
1269 for (
int i : std::as_const(order)) {
1270 result.push_back(keys[i]);
1279 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1285 const std::vector<Key> oldKeys = mKeysByFingerprint;
1287 std::vector<Key> merged;
1288 merged.reserve(keys.size() + mKeysByFingerprint.size());
1289 std::set_union(keys.begin(),
1291 mKeysByFingerprint.begin(),
1292 mKeysByFingerprint.end(),
1293 std::back_inserter(merged),
1294 _detail::ByFingerprint<std::less>());
1296 mKeysByFingerprint = merged;
1298 if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1299 find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1302 std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1304 const auto topologicalSortedList = topological_sort(keys);
1305 for (
const Key &key : topologicalSortedList) {
1307 const char *
const fpr = key.primaryFingerprint();
1308 if (!fpr || !*fpr) {
1312 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1314 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1315 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1316 if (it != mKeysByNonExistingParent.end()) {
1317 mKeysByNonExistingParent.erase(it);
1322 if (!keyAlreadyExisted) {
1323 auto last = mTopLevels.begin();
1324 auto lastFP = mKeysByFingerprint.begin();
1326 for (
const Key &k : children) {
1327 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1328 Q_ASSERT(last != mTopLevels.end());
1329 const int row = std::distance(mTopLevels.begin(), last);
1331 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1332 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1335 if (!modelResetInProgress()) {
1338 last = mTopLevels.erase(last);
1339 lastFP = mKeysByFingerprint.erase(lastFP);
1340 if (!modelResetInProgress()) {
1347 const char *
const issuer_fpr = cleanChainID(key);
1348 if (!issuer_fpr || !*issuer_fpr) {
1350 addTopLevelKey(key);
1351 }
else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1353 addKeyWithParent(issuer_fpr, key);
1356 addKeyWithoutParent(issuer_fpr, key);
1361 while (key_parent.
isValid()) {
1362 changedParents.insert(doMapToKey(key_parent));
1363 key_parent = key_parent.
parent();
1368 if (!keyAlreadyExisted && !children.empty()) {
1373 for (
int i = children.size() - 1; i >= 0; --i) {
1374 Q_EMIT rowMoved(new_parent, i);
1380 if (!modelResetInProgress()) {
1381 for (
const Key &i : std::as_const(changedParents)) {
1388 return indexes(keys);
1391void HierarchicalKeyListModel::doRemoveKey(
const Key &key)
1398 const char *
const fpr = key.primaryFingerprint();
1399 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1401 std::vector<Key> keys = mKeysByFingerprint;
1402 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1403 if (it == keys.end()) {
1416 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1418 Q_ASSERT(it != mKeysByFingerprint.end());
1419 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1420 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1422 if (!modelResetInProgress()) {
1423 beginRemoveRows(parent(idx), idx.
row(), idx.
row());
1425 mKeysByFingerprint.erase(it);
1427 const char *
const issuer_fpr = cleanChainID(key);
1429 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1430 if (tlIt != mTopLevels.end()) {
1431 mTopLevels.erase(tlIt);
1434 if (issuer_fpr && *issuer_fpr) {
1435 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1436 if (nexIt != mKeysByNonExistingParent.end()) {
1437 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1438 if (eit != nexIt->second.end()) {
1439 nexIt->second.erase(eit);
1441 if (nexIt->second.empty()) {
1442 mKeysByNonExistingParent.erase(nexIt);
1446 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1447 if (exIt != mKeysByExistingParent.end()) {
1448 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1449 if (eit != exIt->second.end()) {
1450 exIt->second.erase(eit);
1452 if (exIt->second.empty()) {
1453 mKeysByExistingParent.erase(exIt);
1457 if (!modelResetInProgress()) {
1462KeyGroup HierarchicalKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
1470 if (
static_cast<unsigned>(idx.
row()) >= mTopLevels.size() &&
static_cast<unsigned>(idx.
row()) < mTopLevels.size() + mGroups.size()
1471 && idx.
column() < NumColumns) {
1472 return mGroups[idx.
row() - mTopLevels.size()];
1478QModelIndex HierarchicalKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
1480 Q_ASSERT(!group.isNull());
1481 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
1482 return g.source() == group.source() && g.id() == group.id();
1484 if (it == mGroups.cend()) {
1487 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1491void HierarchicalKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
1493 Q_ASSERT(mGroups.empty());
1494 const int first = mTopLevels.size();
1495 const int last = first + groups.size() - 1;
1496 if (!modelResetInProgress()) {
1500 if (!modelResetInProgress()) {
1505QModelIndex HierarchicalKeyListModel::doAddGroup(
const KeyGroup &group)
1507 const int newRow = lastGroupRow() + 1;
1508 if (!modelResetInProgress()) {
1511 mGroups.push_back(group);
1512 if (!modelResetInProgress()) {
1515 return createIndex(newRow, 0);
1518bool HierarchicalKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
1520 if (group.isNull()) {
1523 const int groupIndex = this->groupIndex(index);
1524 if (groupIndex == -1) {
1527 mGroups[groupIndex] = group;
1528 if (!modelResetInProgress()) {
1529 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
1534bool HierarchicalKeyListModel::doRemoveGroup(
const KeyGroup &group)
1536 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1540 const int groupIndex = this->groupIndex(modelIndex);
1541 Q_ASSERT(groupIndex != -1);
1542 if (groupIndex == -1) {
1545 if (!modelResetInProgress()) {
1548 mGroups.erase(mGroups.begin() + groupIndex);
1549 if (!modelResetInProgress()) {
1555void HierarchicalKeyListModel::doClear(ItemTypes types)
1559 mKeysByFingerprint.clear();
1560 mKeysByExistingParent.clear();
1561 mKeysByNonExistingParent.clear();
1562 Issuers::instance()->clear();
1564 if (types & Groups) {
1569void AbstractKeyListModel::useKeyCache(
bool value, KeyList::Options options)
1571 d->m_keyListOptions = options;
1572 d->m_useKeyCache = value;
1573 if (!d->m_useKeyCache) {
1576 d->updateFromKeyCache();
1578 connect(KeyCache::instance().
get(), &KeyCache::keysMayHaveChanged,
this, [
this] {
1579 d->updateFromKeyCache();
1584AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(
QObject *p)
1586 AbstractKeyListModel *
const m =
new FlatKeyListModel(p);
1587#ifdef KLEO_MODEL_TEST
1594AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(
QObject *p)
1596 AbstractKeyListModel *
const m =
new HierarchicalKeyListModel(p);
1597#ifdef KLEO_MODEL_TEST
1603QMimeData *AbstractKeyListModel::mimeData(
const QModelIndexList &indexes)
const
1605 if (d->m_dragHandler) {
1606 return d->m_dragHandler->mimeData(indexes);
1614 if (d->m_dragHandler) {
1615 return d->m_dragHandler->flags(index);
1621QStringList AbstractKeyListModel::mimeTypes()
const
1623 if (d->m_dragHandler) {
1624 return d->m_dragHandler->mimeTypes();
1630void AbstractKeyListModel::setDragHandler(
const std::shared_ptr<DragHandler> &dragHandler)
1632 d->m_dragHandler = dragHandler;
1635#include "keylistmodel.moc"
1674#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)