1 /* -*- mode: c++; c-basic-offset:4 -*-
2     models/keylistmodel.cpp
3 
4     This file is part of libkleopatra, the KDE keymanagement library
5     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6     SPDX-FileCopyrightText: 2021 g10 Code GmbH
7     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
8 
9     SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "keylistmodel.h"
13 
14 #include "keycache.h"
15 #include "kleo/keygroup.h"
16 #include "kleo/predicates.h"
17 #include "kleo/keyfiltermanager.h"
18 #include "kleo/keyfilter.h"
19 #include "utils/algorithm.h"
20 #include "utils/formatting.h"
21 
22 #ifdef KLEO_MODEL_TEST
23 # include <QAbstractItemModelTester>
24 #endif
25 
26 #include <Libkleo/KeyFilterManager>
27 #include <Libkleo/KeyFilter>
28 
29 #include <KLocalizedString>
30 
31 #include <QFont>
32 #include <QColor>
33 #include <QHash>
34 #include <QIcon>
35 #include <QDate>
36 #include <gpgme++/key.h>
37 
38 #ifndef Q_MOC_RUN // QTBUG-22829
39 #include <boost/graph/topological_sort.hpp>
40 #include <boost/graph/adjacency_list.hpp>
41 #endif
42 
43 #include <algorithm>
44 #include <map>
45 #include <set>
46 #include <iterator>
47 
48 #include <gpgme++/gpgmepp_version.h>
49 #if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
50 # define GPGME_HAS_REMARKS
51 #endif
52 
53 using namespace GpgME;
54 using namespace Kleo;
55 using namespace Kleo::KeyList;
56 
57 Q_DECLARE_METATYPE(GpgME::Key)
58 Q_DECLARE_METATYPE(KeyGroup)
59 
60 class AbstractKeyListModel::Private
61 {
62     AbstractKeyListModel *const q;
63 public:
64     explicit Private(AbstractKeyListModel *qq);
65 
66     void updateFromKeyCache();
67 
68 public:
69     int m_toolTipOptions = Formatting::Validity;
70     mutable QHash<const char *, QVariant> prettyEMailCache;
71     mutable QHash<const char *, QVariant> remarksCache;
72     bool m_useKeyCache = false;
73     bool m_modelResetInProgress = false;
74     KeyList::Options m_keyListOptions = AllKeys;
75     std::vector<GpgME::Key> m_remarkKeys;
76 };
77 
Private(Kleo::AbstractKeyListModel * qq)78 AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
79     : q(qq)
80 {
81 }
82 
updateFromKeyCache()83 void AbstractKeyListModel::Private::updateFromKeyCache()
84 {
85     if (m_useKeyCache) {
86         q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
87         if (m_keyListOptions == IncludeGroups) {
88             q->setGroups(KeyCache::instance()->groups());
89         }
90     }
91 }
92 
AbstractKeyListModel(QObject * p)93 AbstractKeyListModel::AbstractKeyListModel(QObject *p)
94     : QAbstractItemModel(p), KeyListModelInterface(), d(new Private(this))
95 {
96     connect(this, &QAbstractItemModel::modelAboutToBeReset, this, [this] () { d->m_modelResetInProgress = true; });
97     connect(this, &QAbstractItemModel::modelReset, this, [this] () { d->m_modelResetInProgress = false; });
98 }
99 
~AbstractKeyListModel()100 AbstractKeyListModel::~AbstractKeyListModel() {}
101 
setToolTipOptions(int opts)102 void AbstractKeyListModel::setToolTipOptions(int opts)
103 {
104     d->m_toolTipOptions = opts;
105 }
106 
toolTipOptions() const107 int AbstractKeyListModel::toolTipOptions() const
108 {
109     return d->m_toolTipOptions;
110 }
111 
setRemarkKeys(const std::vector<GpgME::Key> & keys)112 void AbstractKeyListModel::setRemarkKeys(const std::vector<GpgME::Key> &keys)
113 {
114     d->m_remarkKeys = keys;
115 }
116 
remarkKeys() const117 std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys() const
118 {
119     return d->m_remarkKeys;
120 }
121 
key(const QModelIndex & idx) const122 Key AbstractKeyListModel::key(const QModelIndex &idx) const
123 {
124     if (idx.isValid()) {
125         return doMapToKey(idx);
126     } else {
127         return Key::null;
128     }
129 }
130 
keys(const QList<QModelIndex> & indexes) const131 std::vector<Key> AbstractKeyListModel::keys(const QList<QModelIndex> &indexes) const
132 {
133     std::vector<Key> result;
134     result.reserve(indexes.size());
135     std::transform(indexes.begin(), indexes.end(),
136                    std::back_inserter(result),
137                    [this](const QModelIndex &idx) {
138                        return this->key(idx);
139                    });
140     result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
141     _detail::remove_duplicates_by_fpr(result);
142     return result;
143 }
144 
group(const QModelIndex & idx) const145 KeyGroup AbstractKeyListModel::group(const QModelIndex &idx) const
146 {
147     if (idx.isValid()) {
148         return doMapToGroup(idx);
149     } else {
150         return KeyGroup();
151     }
152 }
153 
index(const Key & key) const154 QModelIndex AbstractKeyListModel::index(const Key &key) const
155 {
156     return index(key, 0);
157 }
158 
index(const Key & key,int col) const159 QModelIndex AbstractKeyListModel::index(const Key &key, int col) const
160 {
161     if (key.isNull() || col < 0 || col >= NumColumns) {
162         return {};
163     } else {
164         return doMapFromKey(key, col);
165     }
166 }
167 
indexes(const std::vector<Key> & keys) const168 QList<QModelIndex> AbstractKeyListModel::indexes(const std::vector<Key> &keys) const
169 {
170     QList<QModelIndex> result;
171     result.reserve(keys.size());
172     std::transform(keys.begin(), keys.end(),
173                    std::back_inserter(result),
174                    [this](const Key &key) {
175                        return this->index(key);
176                    });
177     return result;
178 }
179 
index(const KeyGroup & group) const180 QModelIndex AbstractKeyListModel::index(const KeyGroup &group) const
181 {
182     return index(group, 0);
183 }
184 
index(const KeyGroup & group,int col) const185 QModelIndex AbstractKeyListModel::index(const KeyGroup &group, int col) const
186 {
187     if (group.isNull() || col < 0 || col >= NumColumns) {
188         return {};
189     } else {
190         return doMapFromGroup(group, col);
191     }
192 }
193 
setKeys(const std::vector<Key> & keys)194 void AbstractKeyListModel::setKeys(const std::vector<Key> &keys)
195 {
196     beginResetModel();
197     clear(Keys);
198     addKeys(keys);
199     endResetModel();
200 }
201 
addKey(const Key & key)202 QModelIndex AbstractKeyListModel::addKey(const Key &key)
203 {
204     const std::vector<Key> vec(1, key);
205     const QList<QModelIndex> l = doAddKeys(vec);
206     return l.empty() ? QModelIndex() : l.front();
207 }
208 
removeKey(const Key & key)209 void AbstractKeyListModel::removeKey(const Key &key)
210 {
211     if (key.isNull()) {
212         return;
213     }
214     doRemoveKey(key);
215     d->prettyEMailCache.remove(key.primaryFingerprint());
216     d->remarksCache.remove(key.primaryFingerprint());
217 }
218 
addKeys(const std::vector<Key> & keys)219 QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
220 {
221     std::vector<Key> sorted;
222     sorted.reserve(keys.size());
223     std::remove_copy_if(keys.begin(), keys.end(),
224                         std::back_inserter(sorted),
225                         std::mem_fn(&Key::isNull));
226     std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
227     return doAddKeys(sorted);
228 }
229 
setGroups(const std::vector<KeyGroup> & groups)230 void AbstractKeyListModel::setGroups(const std::vector<KeyGroup> &groups)
231 {
232     beginResetModel();
233     clear(Groups);
234     doSetGroups(groups);
235     endResetModel();
236 }
237 
addGroup(const KeyGroup & group)238 QModelIndex AbstractKeyListModel::addGroup(const KeyGroup &group)
239 {
240     if (group.isNull()) {
241         return QModelIndex();
242     }
243     return doAddGroup(group);
244 }
245 
removeGroup(const KeyGroup & group)246 bool AbstractKeyListModel::removeGroup(const KeyGroup &group)
247 {
248     if (group.isNull()) {
249         return false;
250     }
251     return doRemoveGroup(group);
252 }
253 
clear(ItemTypes types)254 void AbstractKeyListModel::clear(ItemTypes types)
255 {
256     const bool inReset = modelResetInProgress();
257     if (!inReset) {
258         beginResetModel();
259     }
260     doClear(types);
261     if (types & Keys) {
262         d->prettyEMailCache.clear();
263         d->remarksCache.clear();
264     }
265     if (!inReset) {
266         endResetModel();
267     }
268 }
269 
columnCount(const QModelIndex &) const270 int AbstractKeyListModel::columnCount(const QModelIndex &) const
271 {
272     return NumColumns;
273 }
274 
headerData(int section,Qt::Orientation o,int role) const275 QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const
276 {
277     if (o == Qt::Horizontal) {
278         if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
279             switch (section) {
280             case PrettyName:       return i18n("Name");
281             case PrettyEMail:      return i18n("E-Mail");
282             case Validity:         return i18n("User-IDs");
283             case ValidFrom:        return i18n("Valid From");
284             case ValidUntil:       return i18n("Valid Until");
285             case TechnicalDetails: return i18n("Protocol");
286             case ShortKeyID:       return i18n("Key-ID");
287             case KeyID:            return i18n("Key-ID");
288             case Fingerprint:      return i18n("Fingerprint");
289             case Issuer:           return i18n("Issuer");
290             case SerialNumber:     return i18n("Serial Number");
291             case Origin:           return i18n("Origin");
292             case LastUpdate:       return i18n("Last Update");
293             case OwnerTrust:       return i18n("Certification Trust");
294             case Remarks:          return i18n("Tags");
295             case NumColumns:;
296             }
297         }
298     }
299     return QVariant();
300 }
301 
returnIfValid(const QColor & t)302 static QVariant returnIfValid(const QColor &t)
303 {
304     if (t.isValid()) {
305         return t;
306     } else {
307         return QVariant();
308     }
309 }
310 
returnIfValid(const QIcon & t)311 static QVariant returnIfValid(const QIcon &t)
312 {
313     if (!t.isNull()) {
314         return t;
315     } else {
316         return QVariant();
317     }
318 }
319 
data(const QModelIndex & index,int role) const320 QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
321 {
322     const Key key = this->key(index);
323     if (!key.isNull()) {
324         return data(key, index.column(), role);
325     }
326 
327     const KeyGroup group = this->group(index);
328     if (!group.isNull()) {
329         return data(group, index.column(), role);
330     }
331 
332     return QVariant();
333 }
334 
data(const Key & key,int column,int role) const335 QVariant AbstractKeyListModel::data(const Key &key, int column, int role) const
336 {
337    if (role == Qt::DisplayRole || role == Qt::EditRole) {
338         switch (column) {
339         case PrettyName:
340             return Formatting::prettyName(key);
341         case PrettyEMail:
342             if (const char *const fpr = key.primaryFingerprint()) {
343                 const QHash<const char *, QVariant>::const_iterator it = d->prettyEMailCache.constFind(fpr);
344                 if (it != d->prettyEMailCache.constEnd()) {
345                     return *it;
346                 } else {
347                     return d->prettyEMailCache[fpr] = Formatting::prettyEMail(key);
348                 }
349             } else {
350                 return QVariant();
351             }
352         case Validity:
353             return Formatting::complianceStringShort(key);
354         case ValidFrom:
355             if (role == Qt::EditRole) {
356                 return Formatting::creationDate(key);
357             } else {
358                 return Formatting::creationDateString(key);
359             }
360         case ValidUntil:
361             if (role == Qt::EditRole) {
362                 return Formatting::expirationDate(key);
363             } else {
364                 return Formatting::expirationDateString(key);
365             }
366         case TechnicalDetails:
367             return Formatting::type(key);
368         case ShortKeyID:
369             return QString::fromLatin1(key.shortKeyID());
370         case KeyID:
371             return Formatting::prettyID(key.keyID());
372         case Summary:
373             return Formatting::summaryLine(key);
374         case Fingerprint:
375             return Formatting::prettyID(key.primaryFingerprint());
376         case Issuer:
377             return QString::fromUtf8(key.issuerName());
378         case Origin:
379             return Formatting::origin(key.origin());
380         case LastUpdate:
381             return Formatting::dateString(key.lastUpdate());
382         case SerialNumber:
383             return QString::fromUtf8(key.issuerSerial());
384         case OwnerTrust:
385             return Formatting::ownerTrustShort(key.ownerTrust());
386         case Remarks:
387 #ifdef GPGME_HAS_REMARKS
388             {
389                 const char *const fpr = key.primaryFingerprint();
390                 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() &&
391                         d->m_remarkKeys.size()) {
392                     if (!(key.keyListMode() & GpgME::SignatureNotations)) {
393                         return i18n("Loading...");
394                     }
395                     const QHash<const char *, QVariant>::const_iterator it = d->remarksCache.constFind(fpr);
396                     if (it != d->remarksCache.constEnd()) {
397                         return *it;
398                     } else {
399                         GpgME::Error err;
400                         const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
401                         if (remarks.size() == 1) {
402                             const auto remark = QString::fromStdString(remarks[0]);
403                             return d->remarksCache[fpr] = remark;
404                         } else {
405                             QStringList remarkList;
406                             remarkList.reserve(remarks.size());
407                             for (const auto &rem: remarks) {
408                                 remarkList << QString::fromStdString(rem);
409                             }
410                             const auto remark = remarkList.join(QStringLiteral("; "));
411                             return d->remarksCache[fpr] = remark;
412                         }
413                     }
414                 } else {
415                     return QVariant();
416                 }
417             }
418 #endif
419             return QVariant();
420         case NumColumns:
421             break;
422         }
423     } else if (role == Qt::ToolTipRole) {
424         return Formatting::toolTip(key, toolTipOptions());
425     } else if (role == Qt::FontRole) {
426         return KeyFilterManager::instance()->font(key, (column == ShortKeyID || column == KeyID || column == Fingerprint) ? QFont(QStringLiteral("monospace")) : QFont());
427     } else if (role == Qt::DecorationRole) {
428         return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant();
429     } else if (role == Qt::BackgroundRole) {
430         return returnIfValid(KeyFilterManager::instance()->bgColor(key));
431     } else if (role == Qt::ForegroundRole) {
432         return returnIfValid(KeyFilterManager::instance()->fgColor(key));
433     } else if (role == FingerprintRole) {
434         return QString::fromLatin1(key.primaryFingerprint());
435     } else if (role == KeyRole) {
436         return QVariant::fromValue(key);
437     }
438     return QVariant();
439 }
440 
data(const KeyGroup & group,int column,int role) const441 QVariant AbstractKeyListModel::data(const KeyGroup &group, int column, int role) const
442 {
443     if (role == Qt::DisplayRole || role == Qt::EditRole) {
444         switch (column) {
445         case PrettyName:
446             return group.name();
447         case PrettyEMail:
448             return QVariant();
449         case Validity:
450             return Formatting::complianceStringShort(group);
451         case ValidFrom:
452             return QString();
453         case ValidUntil:
454             return QString();
455         case TechnicalDetails:
456             return Formatting::type(group);
457         case ShortKeyID:
458             return QString();
459         case KeyID:
460             return QString();
461         case Summary:
462             return Formatting::summaryLine(group);  // used for filtering
463         case Fingerprint:
464             return QString();
465         case Issuer:
466             return QString();
467         case Origin:
468             return QString();
469         case LastUpdate:
470             return QString();
471         case SerialNumber:
472             return QString();
473         case OwnerTrust:
474             return QString();
475         case Remarks:
476             return QVariant();
477         case NumColumns:
478             break;
479         }
480     } else if (role == Qt::ToolTipRole) {
481         return Formatting::toolTip(group, toolTipOptions());
482     } else if (role == Qt::FontRole) {
483         return QFont();
484     } else if (role == Qt::DecorationRole) {
485         return column == Icon ? QIcon::fromTheme(QStringLiteral("group")) : QVariant();
486     } else if (role == Qt::BackgroundRole) {
487     } else if (role == Qt::ForegroundRole) {
488     } else if (role == GroupRole) {
489         return QVariant::fromValue(group);
490     }
491     return QVariant();
492 }
493 
setData(const QModelIndex & index,const QVariant & value,int role)494 bool AbstractKeyListModel::setData(const QModelIndex &index, const QVariant &value, int role)
495 {
496     Q_UNUSED(role)
497     Q_ASSERT(value.canConvert<KeyGroup>());
498     if (value.canConvert<KeyGroup>()) {
499         const KeyGroup group = value.value<KeyGroup>();
500         return doSetGroupData(index, group);
501     }
502 
503     return false;
504 }
505 
modelResetInProgress()506 bool AbstractKeyListModel::modelResetInProgress()
507 {
508     return d->m_modelResetInProgress;
509 }
510 
511 namespace
512 {
513 template <typename Base>
514 class TableModelMixin : public Base
515 {
516 public:
TableModelMixin(QObject * p=nullptr)517     explicit TableModelMixin(QObject *p = nullptr) : Base(p) {}
~TableModelMixin()518     ~TableModelMixin() override {}
519 
520     using Base::index;
index(int row,int column,const QModelIndex & pidx=QModelIndex ()) const521     QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const override
522     {
523         return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, nullptr) : QModelIndex();
524     }
525 
526 private:
parent(const QModelIndex &) const527     QModelIndex parent(const QModelIndex &) const override
528     {
529         return QModelIndex();
530     }
hasChildren(const QModelIndex & pidx) const531     bool hasChildren(const QModelIndex &pidx) const override
532     {
533         return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
534     }
535 };
536 
537 class FlatKeyListModel
538 #ifndef Q_MOC_RUN
539     : public TableModelMixin<AbstractKeyListModel>
540 #else
541     : public AbstractKeyListModel
542 #endif
543 {
544     Q_OBJECT
545 public:
546     explicit FlatKeyListModel(QObject *parent = nullptr);
547     ~FlatKeyListModel() override;
548 
rowCount(const QModelIndex & pidx) const549     int rowCount(const QModelIndex &pidx) const override
550     {
551         return pidx.isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
552     }
553 
554 private:
555     Key doMapToKey(const QModelIndex &index) const override;
556     QModelIndex doMapFromKey(const Key &key, int col) const override;
557     QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
558     void doRemoveKey(const Key &key) override;
559 
560     KeyGroup doMapToGroup(const QModelIndex &index) const override;
561     QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
562     void doSetGroups(const std::vector<KeyGroup> &groups) override;
563     QModelIndex doAddGroup(const KeyGroup &group) override;
564     bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
565     bool doRemoveGroup(const KeyGroup &group) override;
566 
doClear(ItemTypes types)567     void doClear(ItemTypes types) override
568     {
569         if (types & Keys) {
570             mKeysByFingerprint.clear();
571         }
572         if (types & Groups) {
573             mGroups.clear();
574         }
575     }
576 
firstGroupRow() const577     int firstGroupRow() const
578     {
579         return mKeysByFingerprint.size();
580     }
581 
lastGroupRow() const582     int lastGroupRow() const
583     {
584         return mKeysByFingerprint.size() + mGroups.size() - 1;
585     }
586 
groupIndex(const QModelIndex & index) const587     int groupIndex(const QModelIndex &index) const
588     {
589         if (!index.isValid()
590                 || index.row() < firstGroupRow() || index.row() > lastGroupRow()
591                 || index.column() >= NumColumns) {
592             return -1;
593         }
594         return index.row() - firstGroupRow();
595     }
596 
597 private:
598     std::vector<Key> mKeysByFingerprint;
599     std::vector<KeyGroup> mGroups;
600 };
601 
602 class HierarchicalKeyListModel : public AbstractKeyListModel
603 {
604     Q_OBJECT
605 public:
606     explicit HierarchicalKeyListModel(QObject *parent = nullptr);
607     ~HierarchicalKeyListModel() override;
608 
609     int rowCount(const QModelIndex &pidx) const override;
610     using AbstractKeyListModel::index;
611     QModelIndex index(int row, int col, const QModelIndex &pidx) const override;
612     QModelIndex parent(const QModelIndex &idx) const override;
613 
hasChildren(const QModelIndex & pidx) const614     bool hasChildren(const QModelIndex &pidx) const override
615     {
616         return rowCount(pidx) > 0;
617     }
618 
619 private:
620     Key doMapToKey(const QModelIndex &index) const override;
621     QModelIndex doMapFromKey(const Key &key, int col) const override;
622     QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
623     void doRemoveKey(const Key &key) override;
624 
625     KeyGroup doMapToGroup(const QModelIndex &index) const override;
626     QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
627     void doSetGroups(const std::vector<KeyGroup> &groups) override;
628     QModelIndex doAddGroup(const KeyGroup &group) override;
629     bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
630     bool doRemoveGroup(const KeyGroup &group) override;
631 
doClear(ItemTypes types)632     void doClear(ItemTypes types) override
633     {
634         if (types & Keys) {
635             mTopLevels.clear();
636             mKeysByFingerprint.clear();
637             mKeysByExistingParent.clear();
638             mKeysByNonExistingParent.clear();
639         }
640         if (types & Groups) {
641             mGroups.clear();
642         }
643     }
644 
firstGroupRow() const645     int firstGroupRow() const
646     {
647         return mTopLevels.size();
648     }
649 
lastGroupRow() const650     int lastGroupRow() const
651     {
652         return mTopLevels.size() + mGroups.size() - 1;
653     }
654 
groupIndex(const QModelIndex & index) const655     int groupIndex(const QModelIndex &index) const
656     {
657         if (!index.isValid()
658                 || index.row() < firstGroupRow() || index.row() > lastGroupRow()
659                 || index.column() >= NumColumns) {
660             return -1;
661         }
662         return index.row() - firstGroupRow();
663     }
664 
665 private:
666     void addTopLevelKey(const Key &key);
667     void addKeyWithParent(const char *issuer_fpr, const Key &key);
668     void addKeyWithoutParent(const char *issuer_fpr, const Key &key);
669 
670 private:
671     typedef std::map< std::string, std::vector<Key> > Map;
672     std::vector<Key> mKeysByFingerprint; // all keys
673     Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
674     std::vector<Key> mTopLevels; // all roots + parent-less
675     std::vector<KeyGroup> mGroups;
676 };
677 
cleanChainID(const Key & key)678 static const char *cleanChainID(const Key &key)
679 {
680     if (key.isRoot()) {
681         return "";
682     }
683     if (const char *chid = key.chainID()) {
684         return chid;
685     }
686     return "";
687 }
688 
689 }
690 
FlatKeyListModel(QObject * p)691 FlatKeyListModel::FlatKeyListModel(QObject *p)
692     : TableModelMixin<AbstractKeyListModel>(p)
693 {
694 }
695 
~FlatKeyListModel()696 FlatKeyListModel::~FlatKeyListModel() {}
697 
doMapToKey(const QModelIndex & idx) const698 Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const
699 {
700     Q_ASSERT(idx.isValid());
701     if (static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) {
702         return mKeysByFingerprint[ idx.row() ];
703     } else {
704         return Key::null;
705     }
706 }
707 
doMapFromKey(const Key & key,int col) const708 QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const
709 {
710     Q_ASSERT(!key.isNull());
711     const std::vector<Key>::const_iterator it
712         = std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
713                            key, _detail::ByFingerprint<std::less>());
714     if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
715         return {};
716     } else {
717         return createIndex(it - mKeysByFingerprint.begin(), col);
718     }
719 }
720 
doAddKeys(const std::vector<Key> & keys)721 QList<QModelIndex> FlatKeyListModel::doAddKeys(const std::vector<Key> &keys)
722 {
723     Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
724 
725     if (keys.empty()) {
726         return QList<QModelIndex>();
727     }
728 
729     for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
730 
731         // find an insertion point:
732         const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
733         const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
734 
735         if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
736             // key existed before - replace with new one:
737             mKeysByFingerprint[idx - 1] = *it;
738             if (!modelResetInProgress()) {
739                 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
740             }
741         } else {
742             // new key - insert:
743             if (!modelResetInProgress()) {
744                 beginInsertRows(QModelIndex(), idx, idx);
745             }
746             mKeysByFingerprint.insert(pos, *it);
747             if (!modelResetInProgress()) {
748                 endInsertRows();
749             }
750         }
751     }
752 
753     return indexes(keys);
754 }
755 
doRemoveKey(const Key & key)756 void FlatKeyListModel::doRemoveKey(const Key &key)
757 {
758     const std::vector<Key>::iterator it
759         = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
760                             key, _detail::ByFingerprint<std::less>());
761     if (it == mKeysByFingerprint.end()) {
762         return;
763     }
764 
765     const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
766     if (!modelResetInProgress()) {
767         beginRemoveRows(QModelIndex(), row, row);
768     }
769     mKeysByFingerprint.erase(it);
770     if (!modelResetInProgress()) {
771         endRemoveRows();
772     }
773 }
774 
doMapToGroup(const QModelIndex & idx) const775 KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const
776 {
777     Q_ASSERT(idx.isValid());
778     if (static_cast<unsigned>(idx.row()) >= mKeysByFingerprint.size()
779             && static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() + mGroups.size()
780             && idx.column() < NumColumns) {
781         return mGroups[ idx.row() - mKeysByFingerprint.size() ];
782     } else {
783         return KeyGroup();
784     }
785 }
786 
doMapFromGroup(const KeyGroup & group,int column) const787 QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
788 {
789     Q_ASSERT(!group.isNull());
790     const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(),
791                                  [group](const KeyGroup &g) {
792                                      return g.source() == group.source() && g.id() == group.id();
793                                  });
794     if (it == mGroups.cend()) {
795         return QModelIndex();
796     } else {
797         return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
798     }
799 }
800 
doSetGroups(const std::vector<KeyGroup> & groups)801 void FlatKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
802 {
803     Q_ASSERT(mGroups.empty());  // ensure that groups have been cleared
804     const int first = mKeysByFingerprint.size();
805     const int last = first + groups.size() - 1;
806     if (!modelResetInProgress()) {
807         beginInsertRows(QModelIndex(), first, last);
808     }
809     mGroups = groups;
810     if (!modelResetInProgress()) {
811         endInsertRows();
812     }
813 }
814 
doAddGroup(const KeyGroup & group)815 QModelIndex FlatKeyListModel::doAddGroup(const KeyGroup &group)
816 {
817     const int newRow = lastGroupRow() + 1;
818     if (!modelResetInProgress()) {
819         beginInsertRows(QModelIndex(), newRow, newRow);
820     }
821     mGroups.push_back(group);
822     if (!modelResetInProgress()) {
823         endInsertRows();
824     }
825     return createIndex(newRow, 0);
826 }
827 
doSetGroupData(const QModelIndex & index,const KeyGroup & group)828 bool FlatKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
829 {
830     if (group.isNull()) {
831         return false;
832     }
833     const int groupIndex = this->groupIndex(index);
834     if (groupIndex == -1) {
835         return false;
836     }
837     mGroups[groupIndex] = group;
838     if (!modelResetInProgress()) {
839         Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
840     }
841     return true;
842 }
843 
doRemoveGroup(const KeyGroup & group)844 bool FlatKeyListModel::doRemoveGroup(const KeyGroup &group)
845 {
846     const QModelIndex modelIndex = doMapFromGroup(group, 0);
847     if (!modelIndex.isValid()) {
848         return false;
849     }
850     const int groupIndex = this->groupIndex(modelIndex);
851     Q_ASSERT(groupIndex != -1);
852     if (groupIndex == -1) {
853         return false;
854     }
855     if (!modelResetInProgress()) {
856         beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
857     }
858     mGroups.erase(mGroups.begin() + groupIndex);
859     if (!modelResetInProgress()) {
860         endRemoveRows();
861     }
862     return true;
863 }
864 
HierarchicalKeyListModel(QObject * p)865 HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p)
866     : AbstractKeyListModel(p),
867       mKeysByFingerprint(),
868       mKeysByExistingParent(),
869       mKeysByNonExistingParent(),
870       mTopLevels()
871 {
872 
873 }
874 
~HierarchicalKeyListModel()875 HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
876 
rowCount(const QModelIndex & pidx) const877 int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const
878 {
879 
880     // toplevel item:
881     if (!pidx.isValid()) {
882         return mTopLevels.size() + mGroups.size();
883     }
884 
885     if (pidx.column() != 0) {
886         return 0;
887     }
888 
889     // non-toplevel item - find the number of subjects for this issuer:
890     const Key issuer = this->key(pidx);
891     const char *const fpr = issuer.primaryFingerprint();
892     if (!fpr || !*fpr) {
893         return 0;
894     }
895     const Map::const_iterator it = mKeysByExistingParent.find(fpr);
896     if (it == mKeysByExistingParent.end()) {
897         return 0;
898     }
899     return it->second.size();
900 }
901 
index(int row,int col,const QModelIndex & pidx) const902 QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const
903 {
904 
905     if (row < 0 || col < 0 || col >= NumColumns) {
906         return {};
907     }
908 
909     // toplevel item:
910     if (!pidx.isValid()) {
911         if (static_cast<unsigned>(row) < mTopLevels.size()) {
912             return index(mTopLevels[row], col);
913         } else if (static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
914             return index(mGroups[row - mTopLevels.size()], col);
915         } else {
916             return QModelIndex();
917         }
918     }
919 
920     // non-toplevel item - find the row'th subject of this key:
921     const Key issuer = this->key(pidx);
922     const char *const fpr = issuer.primaryFingerprint();
923     if (!fpr || !*fpr) {
924         return QModelIndex();
925     }
926     const Map::const_iterator it = mKeysByExistingParent.find(fpr);
927     if (it == mKeysByExistingParent.end() || static_cast<unsigned>(row) >= it->second.size()) {
928         return QModelIndex();
929     }
930     return index(it->second[row], col);
931 }
932 
parent(const QModelIndex & idx) const933 QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const
934 {
935     const Key key = this->key(idx);
936     if (key.isNull() || key.isRoot()) {
937         return {};
938     }
939     const std::vector<Key>::const_iterator it
940         = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
941                             cleanChainID(key), _detail::ByFingerprint<std::less>());
942     return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex();
943 }
944 
doMapToKey(const QModelIndex & idx) const945 Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const
946 {
947 
948     if (!idx.isValid()) {
949         return Key::null;
950     }
951 
952     const char *const issuer_fpr = static_cast<const char *>(idx.internalPointer());
953     if (!issuer_fpr || !*issuer_fpr) {
954         // top-level:
955         if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()) {
956             return Key::null;
957         } else {
958             return mTopLevels[idx.row()];
959         }
960     }
961 
962     // non-toplevel:
963     const Map::const_iterator it
964         = mKeysByExistingParent.find(issuer_fpr);
965     if (it == mKeysByExistingParent.end() || static_cast<unsigned>(idx.row()) >= it->second.size()) {
966         return Key::null;
967     }
968     return it->second[idx.row()];
969 }
970 
doMapFromKey(const Key & key,int col) const971 QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const
972 {
973 
974     if (key.isNull()) {
975         return {};
976     }
977 
978     const char *issuer_fpr = cleanChainID(key);
979 
980     // we need to look in the toplevels list,...
981     const std::vector<Key> *v = &mTopLevels;
982     if (issuer_fpr && *issuer_fpr) {
983         const std::map< std::string, std::vector<Key> >::const_iterator it
984             = mKeysByExistingParent.find(issuer_fpr);
985         // ...unless we find an existing parent:
986         if (it != mKeysByExistingParent.end()) {
987             v = &it->second;
988         } else {
989             issuer_fpr = nullptr;    // force internalPointer to zero for toplevels
990         }
991     }
992 
993     const std::vector<Key>::const_iterator it
994         = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
995     if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
996         return QModelIndex();
997     }
998 
999     const unsigned int row = std::distance(v->begin(), it);
1000     return createIndex(row, col, const_cast<char * /* thanks, Trolls :/ */ >(issuer_fpr));
1001 }
1002 
addKeyWithParent(const char * issuer_fpr,const Key & key)1003 void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key)
1004 {
1005 
1006     Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
1007 
1008     std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1009 
1010     // find insertion point:
1011     const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1012     const int row = std::distance(subjects.begin(), it);
1013 
1014     if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1015         // exists -> replace
1016         *it = key;
1017         if (!modelResetInProgress()) {
1018             Q_EMIT dataChanged(createIndex(row, 0, const_cast<char *>(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast<char *>(issuer_fpr)));
1019         }
1020     } else {
1021         // doesn't exist -> insert
1022         const std::vector<Key>::const_iterator pos = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1023                                                                        issuer_fpr, _detail::ByFingerprint<std::less>());
1024         Q_ASSERT(pos != mKeysByFingerprint.end());
1025         if (!modelResetInProgress()) {
1026             beginInsertRows(index(*pos), row, row);
1027         }
1028         subjects.insert(it, key);
1029         if (!modelResetInProgress()) {
1030             endInsertRows();
1031         }
1032     }
1033 }
1034 
addKeyWithoutParent(const char * issuer_fpr,const Key & key)1035 void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key)
1036 {
1037 
1038     Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
1039 
1040     std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1041 
1042     // find insertion point:
1043     const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1044 
1045     if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0)
1046         // exists -> replace
1047     {
1048         *it = key;
1049     } else
1050         // doesn't exist -> insert
1051     {
1052         subjects.insert(it, key);
1053     }
1054 
1055     addTopLevelKey(key);
1056 }
1057 
addTopLevelKey(const Key & key)1058 void HierarchicalKeyListModel::addTopLevelKey(const Key &key)
1059 {
1060 
1061     // find insertion point:
1062     const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1063     const int row = std::distance(mTopLevels.begin(), it);
1064 
1065     if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1066         // exists -> replace
1067         *it = key;
1068         if (!modelResetInProgress()) {
1069             Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1070         }
1071     } else {
1072         // doesn't exist -> insert
1073         if (!modelResetInProgress()) {
1074             beginInsertRows(QModelIndex(), row, row);
1075         }
1076         mTopLevels.insert(it, key);
1077         if (!modelResetInProgress()) {
1078             endInsertRows();
1079         }
1080     }
1081 
1082 }
1083 
1084 // sorts 'keys' such that parent always come before their children:
topological_sort(const std::vector<Key> & keys)1085 static std::vector<Key> topological_sort(const std::vector<Key> &keys)
1086 {
1087 
1088     boost::adjacency_list<> graph(keys.size());
1089 
1090     // add edges from children to parents:
1091     for (unsigned int i = 0, end = keys.size(); i != end; ++i) {
1092         const char *const issuer_fpr = cleanChainID(keys[i]);
1093         if (!issuer_fpr || !*issuer_fpr) {
1094             continue;
1095         }
1096         const std::vector<Key>::const_iterator it
1097             = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1098         if (it == keys.end()) {
1099             continue;
1100         }
1101         add_edge(i, std::distance(keys.begin(), it), graph);
1102     }
1103 
1104     std::vector<int> order;
1105     order.reserve(keys.size());
1106     topological_sort(graph, std::back_inserter(order));
1107 
1108     Q_ASSERT(order.size() == keys.size());
1109 
1110     std::vector<Key> result;
1111     result.reserve(keys.size());
1112     for (int i : std::as_const(order)) {
1113         result.push_back(keys[i]);
1114     }
1115     return result;
1116 }
1117 
doAddKeys(const std::vector<Key> & keys)1118 QList<QModelIndex> HierarchicalKeyListModel::doAddKeys(const std::vector<Key> &keys)
1119 {
1120     Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1121 
1122     if (keys.empty()) {
1123         return QList<QModelIndex>();
1124     }
1125 
1126     const std::vector<Key> oldKeys = mKeysByFingerprint;
1127 
1128     std::vector<Key> merged;
1129     merged.reserve(keys.size() + mKeysByFingerprint.size());
1130     std::set_union(keys.begin(), keys.end(),
1131                    mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1132                    std::back_inserter(merged), _detail::ByFingerprint<std::less>());
1133 
1134     mKeysByFingerprint = merged;
1135 
1136     std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
1137 
1138     const auto topologicalSortedList = topological_sort(keys);
1139     for (const Key &key : topologicalSortedList) {
1140 
1141         // check to see whether this key is a parent for a previously parent-less group:
1142         const char *const fpr = key.primaryFingerprint();
1143         if (!fpr || !*fpr) {
1144             continue;
1145         }
1146 
1147         const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1148 
1149         const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1150         const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1151         if (it != mKeysByNonExistingParent.end()) {
1152             mKeysByNonExistingParent.erase(it);
1153         }
1154 
1155         // Step 1: For new keys, remove children from toplevel:
1156 
1157         if (!keyAlreadyExisted) {
1158             auto last = mTopLevels.begin();
1159             auto lastFP = mKeysByFingerprint.begin();
1160 
1161             for (const Key &k : std::as_const(children)) {
1162                 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1163                 Q_ASSERT(last != mTopLevels.end());
1164                 const int row = std::distance(mTopLevels.begin(), last);
1165 
1166                 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1167                 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1168 
1169                 Q_EMIT rowAboutToBeMoved(QModelIndex(), row);
1170                 if (!modelResetInProgress()) {
1171                     beginRemoveRows(QModelIndex(), row, row);
1172                 }
1173                 last = mTopLevels.erase(last);
1174                 lastFP = mKeysByFingerprint.erase(lastFP);
1175                 if (!modelResetInProgress()) {
1176                     endRemoveRows();
1177                 }
1178             }
1179         }
1180         // Step 2: add/update key
1181 
1182         const char *const issuer_fpr = cleanChainID(key);
1183         if (!issuer_fpr || !*issuer_fpr)
1184             // root or something...
1185         {
1186             addTopLevelKey(key);
1187         } else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>()))
1188             // parent exists...
1189         {
1190             addKeyWithParent(issuer_fpr, key);
1191         } else
1192             // parent doesn't exist yet...
1193         {
1194             addKeyWithoutParent(issuer_fpr, key);
1195         }
1196 
1197         const QModelIndex key_idx = index(key);
1198         QModelIndex key_parent = key_idx.parent();
1199         while (key_parent.isValid()) {
1200             changedParents.insert(doMapToKey(key_parent));
1201             key_parent = key_parent.parent();
1202         }
1203 
1204         // Step 3: Add children to new parent ( == key )
1205 
1206         if (!keyAlreadyExisted && !children.empty()) {
1207             addKeys(children);
1208             const QModelIndex new_parent = index(key);
1209             // Q_EMIT the rowMoved() signals in reversed direction, so the
1210             // implementation can use a stack for mapping.
1211             for (int i = children.size() - 1; i >= 0; --i) {
1212                 Q_EMIT rowMoved(new_parent, i);
1213             }
1214         }
1215     }
1216     //Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
1217     //show a parent node if it just got children matching the proxy's filter
1218     if (!modelResetInProgress()) {
1219         for (const Key &i : std::as_const(changedParents)) {
1220             const QModelIndex idx = index(i);
1221             if (idx.isValid()) {
1222                 Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1));
1223             }
1224         }
1225     }
1226     return indexes(keys);
1227 }
1228 
doRemoveKey(const Key & key)1229 void HierarchicalKeyListModel::doRemoveKey(const Key &key)
1230 {
1231     const QModelIndex idx = index(key);
1232     if (!idx.isValid()) {
1233         return;
1234     }
1235 
1236     const char *const fpr = key.primaryFingerprint();
1237     if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1238         //handle non-leave nodes:
1239         std::vector<Key> keys = mKeysByFingerprint;
1240         const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(),
1241                                                                 key, _detail::ByFingerprint<std::less>());
1242         if (it == keys.end()) {
1243             return;
1244         }
1245         keys.erase(it);
1246         // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
1247         // but acceptable given that deletion of non-leave nodes is rather rare.
1248         clear(Keys);
1249         addKeys(keys);
1250         return;
1251     }
1252 
1253     //handle leave nodes:
1254 
1255     const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1256                                                             key, _detail::ByFingerprint<std::less>());
1257 
1258     Q_ASSERT(it != mKeysByFingerprint.end());
1259     Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1260     Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1261 
1262     if (!modelResetInProgress()) {
1263         beginRemoveRows(parent(idx), idx.row(), idx.row());
1264     }
1265     mKeysByFingerprint.erase(it);
1266 
1267     const char *const issuer_fpr = cleanChainID(key);
1268 
1269     const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(),
1270                                                               key, _detail::ByFingerprint<std::less>());
1271     if (tlIt != mTopLevels.end()) {
1272         mTopLevels.erase(tlIt);
1273     }
1274 
1275     if (issuer_fpr && *issuer_fpr) {
1276         const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1277         if (nexIt != mKeysByNonExistingParent.end()) {
1278             const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(),
1279                                                                      key, _detail::ByFingerprint<std::less>());
1280             if (eit != nexIt->second.end()) {
1281                 nexIt->second.erase(eit);
1282             }
1283             if (nexIt->second.empty()) {
1284                 mKeysByNonExistingParent.erase(nexIt);
1285             }
1286         }
1287 
1288         const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1289         if (exIt != mKeysByExistingParent.end()) {
1290             const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(),
1291                                                                      key, _detail::ByFingerprint<std::less>());
1292             if (eit != exIt->second.end()) {
1293                 exIt->second.erase(eit);
1294             }
1295             if (exIt->second.empty()) {
1296                 mKeysByExistingParent.erase(exIt);
1297             }
1298         }
1299     }
1300     if (!modelResetInProgress()) {
1301         endRemoveRows();
1302     }
1303 }
1304 
doMapToGroup(const QModelIndex & idx) const1305 KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const
1306 {
1307     Q_ASSERT(idx.isValid());
1308     if (idx.parent().isValid()) {
1309         // groups are always top-level
1310         return KeyGroup();
1311     }
1312 
1313     if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()
1314             && static_cast<unsigned>(idx.row()) < mTopLevels.size() + mGroups.size()
1315             && idx.column() < NumColumns) {
1316         return mGroups[ idx.row() - mTopLevels.size() ];
1317     } else {
1318         return KeyGroup();
1319     }
1320 }
1321 
doMapFromGroup(const KeyGroup & group,int column) const1322 QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
1323 {
1324     Q_ASSERT(!group.isNull());
1325     const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(),
1326                                  [group](const KeyGroup &g) {
1327                                      return g.source() == group.source() && g.id() == group.id();
1328                                  });
1329     if (it == mGroups.cend()) {
1330         return QModelIndex();
1331     } else {
1332         return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1333     }
1334 }
1335 
doSetGroups(const std::vector<KeyGroup> & groups)1336 void HierarchicalKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
1337 {
1338     Q_ASSERT(mGroups.empty());  // ensure that groups have been cleared
1339     const int first = mTopLevels.size();
1340     const int last = first + groups.size() - 1;
1341     if (!modelResetInProgress()) {
1342         beginInsertRows(QModelIndex(), first, last);
1343     }
1344     mGroups = groups;
1345     if (!modelResetInProgress()) {
1346         endInsertRows();
1347     }
1348 }
1349 
doAddGroup(const KeyGroup & group)1350 QModelIndex HierarchicalKeyListModel::doAddGroup(const KeyGroup &group)
1351 {
1352     const int newRow = lastGroupRow() + 1;
1353     if (!modelResetInProgress()) {
1354         beginInsertRows(QModelIndex(), newRow, newRow);
1355     }
1356     mGroups.push_back(group);
1357     if (!modelResetInProgress()) {
1358         endInsertRows();
1359     }
1360     return createIndex(newRow, 0);
1361 }
1362 
doSetGroupData(const QModelIndex & index,const KeyGroup & group)1363 bool HierarchicalKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
1364 {
1365     if (group.isNull()) {
1366         return false;
1367     }
1368     const int groupIndex = this->groupIndex(index);
1369     if (groupIndex == -1) {
1370         return false;
1371     }
1372     mGroups[groupIndex] = group;
1373     if (!modelResetInProgress()) {
1374         Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
1375     }
1376     return true;
1377 }
1378 
doRemoveGroup(const KeyGroup & group)1379 bool HierarchicalKeyListModel::doRemoveGroup(const KeyGroup &group)
1380 {
1381     const QModelIndex modelIndex = doMapFromGroup(group, 0);
1382     if (!modelIndex.isValid()) {
1383         return false;
1384     }
1385     const int groupIndex = this->groupIndex(modelIndex);
1386     Q_ASSERT(groupIndex != -1);
1387     if (groupIndex == -1) {
1388         return false;
1389     }
1390     if (!modelResetInProgress()) {
1391         beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
1392     }
1393     mGroups.erase(mGroups.begin() + groupIndex);
1394     if (!modelResetInProgress()) {
1395         endRemoveRows();
1396     }
1397     return true;
1398 }
1399 
useKeyCache(bool value,KeyList::Options options)1400 void AbstractKeyListModel::useKeyCache(bool value, KeyList::Options options)
1401 {
1402     d->m_keyListOptions = options;
1403     d->m_useKeyCache = value;
1404     if (!d->m_useKeyCache) {
1405         clear(All);
1406     } else {
1407         d->updateFromKeyCache();
1408     }
1409     connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged,
1410             this, [this] { d->updateFromKeyCache(); });
1411 }
1412 
1413 // static
createFlatKeyListModel(QObject * p)1414 AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p)
1415 {
1416     AbstractKeyListModel *const m = new FlatKeyListModel(p);
1417 #ifdef KLEO_MODEL_TEST
1418     new QAbstractItemModelTester(m, p);
1419 #endif
1420     return m;
1421 }
1422 
1423 // static
createHierarchicalKeyListModel(QObject * p)1424 AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p)
1425 {
1426     AbstractKeyListModel *const m = new HierarchicalKeyListModel(p);
1427 #ifdef KLEO_MODEL_TEST
1428     new QAbstractItemModelTester(m, p);
1429 #endif
1430     return m;
1431 }
1432 
1433 #include "keylistmodel.moc"
1434 
1435 /*!
1436   \fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
1437 
1438   Emitted before the removal of a row from that model. It will later
1439   be added to the model again, in response to which rowMoved() will be
1440   emitted. If multiple rows are moved in one go, multiple
1441   rowAboutToBeMoved() signals are emitted before the corresponding
1442   number of rowMoved() signals is emitted - in reverse order.
1443 
1444   This works around the absence of move semantics in
1445   QAbstractItemModel. Clients can maintain a stack to perform the
1446   QModelIndex-mapping themselves, or, e.g., to preserve the selection
1447   status of the row:
1448 
1449   \code
1450   std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
1451   // ...
1452   void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
1453       mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
1454   }
1455   void slotRowMoved( const QModelIndex & p, int row ) {
1456       const bool wasSelected = mMovingRowWasSelected.back();
1457       mMovingRowWasSelected.pop_back();
1458       if ( wasSelected )
1459           selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
1460   }
1461   \endcode
1462 
1463   A similar mechanism could be used to preserve the current item during moves.
1464 */
1465 
1466 /*!
1467   \fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
1468 
1469   See rowAboutToBeMoved()
1470 */
1471