File indexing completed on 2025-01-05 04:55:45

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     models/keylistmodel.cpp
0003 
0004     This file is part of libkleopatra, the KDE keymanagement library
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0007     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include <config-libkleo.h>
0013 
0014 #include "keylistmodel.h"
0015 
0016 #include "keycache.h"
0017 
0018 #include <libkleo/algorithm.h>
0019 #include <libkleo/formatting.h>
0020 #include <libkleo/keyfilter.h>
0021 #include <libkleo/keyfiltermanager.h>
0022 #include <libkleo/predicates.h>
0023 #include <libkleo/systeminfo.h>
0024 
0025 #include <KLocalizedString>
0026 
0027 #ifdef KLEO_MODEL_TEST
0028 #include <QAbstractItemModelTester>
0029 #endif
0030 #include <QColor>
0031 #include <QDate>
0032 #include <QFont>
0033 #include <QHash>
0034 #include <QIcon>
0035 #include <QMimeData>
0036 
0037 #include <gpgme++/key.h>
0038 
0039 #ifndef Q_MOC_RUN // QTBUG-22829
0040 #include <boost/graph/adjacency_list.hpp>
0041 #include <boost/graph/topological_sort.hpp>
0042 #endif
0043 
0044 #include <algorithm>
0045 #include <iterator>
0046 #include <map>
0047 #include <set>
0048 
0049 using namespace GpgME;
0050 using namespace Kleo;
0051 using namespace Kleo::KeyList;
0052 
0053 #if !UNITY_BUILD
0054 Q_DECLARE_METATYPE(GpgME::Key)
0055 Q_DECLARE_METATYPE(KeyGroup)
0056 #endif
0057 
0058 class AbstractKeyListModel::Private
0059 {
0060     AbstractKeyListModel *const q;
0061 
0062 public:
0063     explicit Private(AbstractKeyListModel *qq);
0064 
0065     void updateFromKeyCache();
0066 
0067     QString getEMail(const Key &key) const;
0068 
0069 public:
0070     int m_toolTipOptions = Formatting::Validity;
0071     mutable QHash<const char *, QString> prettyEMailCache;
0072     mutable QHash<const char *, QVariant> remarksCache;
0073     bool m_useKeyCache = false;
0074     bool m_modelResetInProgress = false;
0075     KeyList::Options m_keyListOptions = AllKeys;
0076     std::vector<GpgME::Key> m_remarkKeys;
0077     std::shared_ptr<DragHandler> m_dragHandler;
0078 };
0079 
0080 AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
0081     : q(qq)
0082 {
0083 }
0084 
0085 void AbstractKeyListModel::Private::updateFromKeyCache()
0086 {
0087     if (m_useKeyCache) {
0088         const bool inReset = q->modelResetInProgress();
0089         if (!inReset) {
0090             q->beginResetModel();
0091         }
0092         q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
0093         if (m_keyListOptions == IncludeGroups) {
0094             q->setGroups(KeyCache::instance()->groups());
0095         }
0096         if (!inReset) {
0097             q->endResetModel();
0098         }
0099     }
0100 }
0101 
0102 QString AbstractKeyListModel::Private::getEMail(const Key &key) const
0103 {
0104     QString email;
0105     if (const auto fpr = key.primaryFingerprint()) {
0106         const auto it = prettyEMailCache.constFind(fpr);
0107         if (it != prettyEMailCache.constEnd()) {
0108             email = *it;
0109         } else {
0110             email = Formatting::prettyEMail(key);
0111             prettyEMailCache[fpr] = email;
0112         }
0113     }
0114     return email;
0115 }
0116 
0117 AbstractKeyListModel::AbstractKeyListModel(QObject *p)
0118     : QAbstractItemModel(p)
0119     , KeyListModelInterface()
0120     , d(new Private(this))
0121 {
0122     connect(this, &QAbstractItemModel::modelAboutToBeReset, this, [this]() {
0123         d->m_modelResetInProgress = true;
0124     });
0125     connect(this, &QAbstractItemModel::modelReset, this, [this]() {
0126         d->m_modelResetInProgress = false;
0127     });
0128 }
0129 
0130 AbstractKeyListModel::~AbstractKeyListModel()
0131 {
0132 }
0133 
0134 void AbstractKeyListModel::setToolTipOptions(int opts)
0135 {
0136     d->m_toolTipOptions = opts;
0137 }
0138 
0139 int AbstractKeyListModel::toolTipOptions() const
0140 {
0141     return d->m_toolTipOptions;
0142 }
0143 
0144 void AbstractKeyListModel::setRemarkKeys(const std::vector<GpgME::Key> &keys)
0145 {
0146     d->m_remarkKeys = keys;
0147 }
0148 
0149 std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys() const
0150 {
0151     return d->m_remarkKeys;
0152 }
0153 
0154 Key AbstractKeyListModel::key(const QModelIndex &idx) const
0155 {
0156     Key key = Key::null;
0157     if (idx.isValid()) {
0158         key = doMapToKey(idx);
0159     }
0160     return key;
0161 }
0162 
0163 std::vector<Key> AbstractKeyListModel::keys(const QList<QModelIndex> &indexes) const
0164 {
0165     std::vector<Key> result;
0166     result.reserve(indexes.size());
0167     std::transform(indexes.begin(), //
0168                    indexes.end(),
0169                    std::back_inserter(result),
0170                    [this](const QModelIndex &idx) {
0171                        return this->key(idx);
0172                    });
0173     result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
0174     _detail::remove_duplicates_by_fpr(result);
0175     return result;
0176 }
0177 
0178 KeyGroup AbstractKeyListModel::group(const QModelIndex &idx) const
0179 {
0180     if (idx.isValid()) {
0181         return doMapToGroup(idx);
0182     } else {
0183         return KeyGroup();
0184     }
0185 }
0186 
0187 QModelIndex AbstractKeyListModel::index(const Key &key) const
0188 {
0189     return index(key, 0);
0190 }
0191 
0192 QModelIndex AbstractKeyListModel::index(const Key &key, int col) const
0193 {
0194     if (key.isNull() || col < 0 || col >= NumColumns) {
0195         return {};
0196     } else {
0197         return doMapFromKey(key, col);
0198     }
0199 }
0200 
0201 QList<QModelIndex> AbstractKeyListModel::indexes(const std::vector<Key> &keys) const
0202 {
0203     QList<QModelIndex> result;
0204     result.reserve(keys.size());
0205     std::transform(keys.begin(), //
0206                    keys.end(),
0207                    std::back_inserter(result),
0208                    [this](const Key &key) {
0209                        return this->index(key);
0210                    });
0211     return result;
0212 }
0213 
0214 QModelIndex AbstractKeyListModel::index(const KeyGroup &group) const
0215 {
0216     return index(group, 0);
0217 }
0218 
0219 QModelIndex AbstractKeyListModel::index(const KeyGroup &group, int col) const
0220 {
0221     if (group.isNull() || col < 0 || col >= NumColumns) {
0222         return {};
0223     } else {
0224         return doMapFromGroup(group, col);
0225     }
0226 }
0227 
0228 void AbstractKeyListModel::setKeys(const std::vector<Key> &keys)
0229 {
0230     const bool inReset = modelResetInProgress();
0231     if (!inReset) {
0232         beginResetModel();
0233     }
0234     clear(Keys);
0235     addKeys(keys);
0236     if (!inReset) {
0237         endResetModel();
0238     }
0239 }
0240 
0241 QModelIndex AbstractKeyListModel::addKey(const Key &key)
0242 {
0243     const std::vector<Key> vec(1, key);
0244     const QList<QModelIndex> l = doAddKeys(vec);
0245     return l.empty() ? QModelIndex() : l.front();
0246 }
0247 
0248 void AbstractKeyListModel::removeKey(const Key &key)
0249 {
0250     if (key.isNull()) {
0251         return;
0252     }
0253     doRemoveKey(key);
0254     d->prettyEMailCache.remove(key.primaryFingerprint());
0255     d->remarksCache.remove(key.primaryFingerprint());
0256 }
0257 
0258 QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
0259 {
0260     std::vector<Key> sorted;
0261     sorted.reserve(keys.size());
0262     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull));
0263     std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
0264     return doAddKeys(sorted);
0265 }
0266 
0267 void AbstractKeyListModel::setGroups(const std::vector<KeyGroup> &groups)
0268 {
0269     const bool inReset = modelResetInProgress();
0270     if (!inReset) {
0271         beginResetModel();
0272     }
0273     clear(Groups);
0274     doSetGroups(groups);
0275     if (!inReset) {
0276         endResetModel();
0277     }
0278 }
0279 
0280 QModelIndex AbstractKeyListModel::addGroup(const KeyGroup &group)
0281 {
0282     if (group.isNull()) {
0283         return QModelIndex();
0284     }
0285     return doAddGroup(group);
0286 }
0287 
0288 bool AbstractKeyListModel::removeGroup(const KeyGroup &group)
0289 {
0290     if (group.isNull()) {
0291         return false;
0292     }
0293     return doRemoveGroup(group);
0294 }
0295 
0296 void AbstractKeyListModel::clear(ItemTypes types)
0297 {
0298     const bool inReset = modelResetInProgress();
0299     if (!inReset) {
0300         beginResetModel();
0301     }
0302     doClear(types);
0303     if (types & Keys) {
0304         d->prettyEMailCache.clear();
0305         d->remarksCache.clear();
0306     }
0307     if (!inReset) {
0308         endResetModel();
0309     }
0310 }
0311 
0312 int AbstractKeyListModel::columnCount(const QModelIndex &) const
0313 {
0314     return NumColumns;
0315 }
0316 
0317 QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const
0318 {
0319     if (o == Qt::Horizontal) {
0320         if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
0321             switch (section) {
0322             case PrettyName:
0323                 return i18nc("@title:column", "Name");
0324             case PrettyEMail:
0325                 return i18nc("@title:column", "E-Mail");
0326             case Validity:
0327                 return i18nc("@title:column", "User-IDs");
0328             case ValidFrom:
0329                 return i18nc("@title:column", "Valid From");
0330             case ValidUntil:
0331                 return i18nc("@title:column", "Valid Until");
0332             case TechnicalDetails:
0333                 return i18nc("@title:column", "Protocol");
0334             case ShortKeyID:
0335                 return i18nc("@title:column", "Key-ID");
0336             case KeyID:
0337                 return i18nc("@title:column", "Key-ID");
0338             case Fingerprint:
0339                 return i18nc("@title:column", "Fingerprint");
0340             case Issuer:
0341                 return i18nc("@title:column", "Issuer");
0342             case SerialNumber:
0343                 return i18nc("@title:column", "Serial Number");
0344             case Origin:
0345                 return i18nc("@title:column", "Origin");
0346             case LastUpdate:
0347                 return i18nc("@title:column", "Last Update");
0348             case OwnerTrust:
0349                 return i18nc("@title:column", "Certification Trust");
0350             case Remarks:
0351                 return i18nc("@title:column", "Tags");
0352             case Algorithm:
0353                 return i18nc("@title:column", "Algorithm");
0354             case Keygrip:
0355                 return i18nc("@title:column", "Keygrip");
0356             case NumColumns:;
0357             }
0358         }
0359     }
0360     return QVariant();
0361 }
0362 
0363 static QVariant returnIfValid(const QColor &t)
0364 {
0365     if (t.isValid()) {
0366         return t;
0367     } else {
0368         return QVariant();
0369     }
0370 }
0371 
0372 static QVariant returnIfValid(const QIcon &t)
0373 {
0374     if (!t.isNull()) {
0375         return t;
0376     } else {
0377         return QVariant();
0378     }
0379 }
0380 
0381 QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
0382 {
0383     const Key key = this->key(index);
0384     if (!key.isNull()) {
0385         return data(key, index.column(), role);
0386     }
0387 
0388     const KeyGroup group = this->group(index);
0389     if (!group.isNull()) {
0390         return data(group, index.column(), role);
0391     }
0392 
0393     return QVariant();
0394 }
0395 
0396 QVariant AbstractKeyListModel::data(const Key &key, int column, int role) const
0397 {
0398     if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::AccessibleTextRole) {
0399         switch (column) {
0400         case PrettyName: {
0401             const auto name = Formatting::prettyName(key);
0402             if (role == Qt::AccessibleTextRole) {
0403                 return name.isEmpty() ? i18nc("text for screen readers for an empty name", "no name") : name;
0404             }
0405             return name;
0406         }
0407         case PrettyEMail: {
0408             const auto email = d->getEMail(key);
0409             if (role == Qt::AccessibleTextRole) {
0410                 return email.isEmpty() ? i18nc("text for screen readers for an empty email address", "no email") : email;
0411             }
0412             return email;
0413         }
0414         case Validity:
0415             return Formatting::complianceStringShort(key);
0416         case ValidFrom:
0417             if (role == Qt::EditRole) {
0418                 return Formatting::creationDate(key);
0419             } else if (role == Qt::AccessibleTextRole) {
0420                 return Formatting::accessibleCreationDate(key);
0421             } else {
0422                 return Formatting::creationDateString(key);
0423             }
0424         case ValidUntil:
0425             if (role == Qt::EditRole) {
0426                 return Formatting::expirationDate(key);
0427             } else if (role == Qt::AccessibleTextRole) {
0428                 return Formatting::accessibleExpirationDate(key);
0429             } else {
0430                 return Formatting::expirationDateString(key);
0431             }
0432         case TechnicalDetails:
0433             return Formatting::type(key);
0434         case ShortKeyID:
0435             if (role == Qt::AccessibleTextRole) {
0436                 return Formatting::accessibleHexID(key.shortKeyID());
0437             } else {
0438                 return Formatting::prettyID(key.shortKeyID());
0439             }
0440         case KeyID:
0441             if (role == Qt::AccessibleTextRole) {
0442                 return Formatting::accessibleHexID(key.keyID());
0443             } else {
0444                 return Formatting::prettyID(key.keyID());
0445             }
0446         case Summary:
0447             return Formatting::summaryLine(key);
0448         case Fingerprint:
0449             if (role == Qt::AccessibleTextRole) {
0450                 return Formatting::accessibleHexID(key.primaryFingerprint());
0451             } else {
0452                 return Formatting::prettyID(key.primaryFingerprint());
0453             }
0454         case Issuer:
0455             return QString::fromUtf8(key.issuerName());
0456         case Origin:
0457             return Formatting::origin(key.origin());
0458         case LastUpdate:
0459             if (role == Qt::AccessibleTextRole) {
0460                 return Formatting::accessibleDate(key.lastUpdate());
0461             } else {
0462                 return Formatting::dateString(key.lastUpdate());
0463             }
0464         case SerialNumber:
0465             return QString::fromUtf8(key.issuerSerial());
0466         case OwnerTrust:
0467             return Formatting::ownerTrustShort(key.ownerTrust());
0468         case Remarks: {
0469             const char *const fpr = key.primaryFingerprint();
0470             if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
0471                 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
0472                     return i18n("Loading...");
0473                 }
0474                 const QHash<const char *, QVariant>::const_iterator it = d->remarksCache.constFind(fpr);
0475                 if (it != d->remarksCache.constEnd()) {
0476                     return *it;
0477                 } else {
0478                     GpgME::Error err;
0479                     const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
0480                     if (remarks.size() == 1) {
0481                         const auto remark = QString::fromStdString(remarks[0]);
0482                         return d->remarksCache[fpr] = remark;
0483                     } else {
0484                         QStringList remarkList;
0485                         remarkList.reserve(remarks.size());
0486                         for (const auto &rem : remarks) {
0487                             remarkList << QString::fromStdString(rem);
0488                         }
0489                         const auto remark = remarkList.join(QStringLiteral("; "));
0490                         return d->remarksCache[fpr] = remark;
0491                     }
0492                 }
0493             } else {
0494                 return QVariant();
0495             }
0496         }
0497             return QVariant();
0498         case Algorithm:
0499             return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
0500         case Keygrip:
0501             if (role == Qt::AccessibleTextRole) {
0502                 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
0503             } else {
0504                 return Formatting::prettyID(key.subkey(0).keyGrip());
0505             }
0506         case NumColumns:
0507             break;
0508         }
0509     } else if (role == Qt::ToolTipRole) {
0510         return Formatting::toolTip(key, toolTipOptions());
0511     } else if (role == Qt::FontRole) {
0512         return KeyFilterManager::instance()->font(key,
0513                                                   (column == ShortKeyID || column == KeyID || column == Fingerprint) ? QFont(QStringLiteral("monospace"))
0514                                                                                                                      : QFont());
0515     } else if (role == Qt::DecorationRole) {
0516         return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant();
0517     } else if (role == Qt::BackgroundRole) {
0518         if (!SystemInfo::isHighContrastModeActive()) {
0519             return returnIfValid(KeyFilterManager::instance()->bgColor(key));
0520         }
0521     } else if (role == Qt::ForegroundRole) {
0522         if (!SystemInfo::isHighContrastModeActive()) {
0523             return returnIfValid(KeyFilterManager::instance()->fgColor(key));
0524         }
0525     } else if (role == FingerprintRole) {
0526         return QString::fromLatin1(key.primaryFingerprint());
0527     } else if (role == KeyRole) {
0528         return QVariant::fromValue(key);
0529     }
0530     return QVariant();
0531 }
0532 
0533 QVariant AbstractKeyListModel::data(const KeyGroup &group, int column, int role) const
0534 {
0535     if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::AccessibleTextRole) {
0536         switch (column) {
0537         case PrettyName:
0538             return group.name();
0539         case Validity:
0540             return Formatting::complianceStringShort(group);
0541         case TechnicalDetails:
0542             return Formatting::type(group);
0543         case Summary:
0544             return Formatting::summaryLine(group); // used for filtering
0545         case PrettyEMail:
0546         case ValidFrom:
0547         case ValidUntil:
0548         case ShortKeyID:
0549         case KeyID:
0550         case Fingerprint:
0551         case Issuer:
0552         case Origin:
0553         case LastUpdate:
0554         case SerialNumber:
0555         case OwnerTrust:
0556         case Remarks:
0557             if (role == Qt::AccessibleTextRole) {
0558                 return i18nc("text for screen readers", "not applicable");
0559             }
0560             break;
0561         case NumColumns:
0562             break;
0563         }
0564     } else if (role == Qt::ToolTipRole) {
0565         return Formatting::toolTip(group, toolTipOptions());
0566     } else if (role == Qt::FontRole) {
0567         return QFont();
0568     } else if (role == Qt::DecorationRole) {
0569         return column == Icon ? QIcon::fromTheme(QStringLiteral("group")) : QVariant();
0570     } else if (role == Qt::BackgroundRole) {
0571     } else if (role == Qt::ForegroundRole) {
0572     } else if (role == GroupRole) {
0573         return QVariant::fromValue(group);
0574     }
0575     return QVariant();
0576 }
0577 
0578 bool AbstractKeyListModel::setData(const QModelIndex &index, const QVariant &value, int role)
0579 {
0580     Q_UNUSED(role)
0581     Q_ASSERT(value.canConvert<KeyGroup>());
0582     if (value.canConvert<KeyGroup>()) {
0583         const KeyGroup group = value.value<KeyGroup>();
0584         return doSetGroupData(index, group);
0585     }
0586 
0587     return false;
0588 }
0589 
0590 bool AbstractKeyListModel::modelResetInProgress()
0591 {
0592     return d->m_modelResetInProgress;
0593 }
0594 
0595 namespace
0596 {
0597 template<typename Base>
0598 class TableModelMixin : public Base
0599 {
0600 public:
0601     explicit TableModelMixin(QObject *p = nullptr)
0602         : Base(p)
0603     {
0604     }
0605     ~TableModelMixin() override
0606     {
0607     }
0608 
0609     using Base::index;
0610     QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const override
0611     {
0612         return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, nullptr) : QModelIndex();
0613     }
0614 
0615 private:
0616     QModelIndex parent(const QModelIndex &) const override
0617     {
0618         return QModelIndex();
0619     }
0620     bool hasChildren(const QModelIndex &pidx) const override
0621     {
0622         return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
0623     }
0624 };
0625 
0626 class FlatKeyListModel
0627 #ifndef Q_MOC_RUN
0628     : public TableModelMixin<AbstractKeyListModel>
0629 #else
0630     : public AbstractKeyListModel
0631 #endif
0632 {
0633     Q_OBJECT
0634 public:
0635     explicit FlatKeyListModel(QObject *parent = nullptr);
0636     ~FlatKeyListModel() override;
0637 
0638     int rowCount(const QModelIndex &pidx) const override
0639     {
0640         return pidx.isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
0641     }
0642 
0643 private:
0644     Key doMapToKey(const QModelIndex &index) const override;
0645     QModelIndex doMapFromKey(const Key &key, int col) const override;
0646     QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
0647     void doRemoveKey(const Key &key) override;
0648 
0649     KeyGroup doMapToGroup(const QModelIndex &index) const override;
0650     QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
0651     void doSetGroups(const std::vector<KeyGroup> &groups) override;
0652     QModelIndex doAddGroup(const KeyGroup &group) override;
0653     bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
0654     bool doRemoveGroup(const KeyGroup &group) override;
0655 
0656     void doClear(ItemTypes types) override
0657     {
0658         if (types & Keys) {
0659             mKeysByFingerprint.clear();
0660         }
0661         if (types & Groups) {
0662             mGroups.clear();
0663         }
0664     }
0665 
0666     int firstGroupRow() const
0667     {
0668         return mKeysByFingerprint.size();
0669     }
0670 
0671     int lastGroupRow() const
0672     {
0673         return mKeysByFingerprint.size() + mGroups.size() - 1;
0674     }
0675 
0676     int groupIndex(const QModelIndex &index) const
0677     {
0678         if (!index.isValid() || index.row() < firstGroupRow() || index.row() > lastGroupRow() || index.column() >= NumColumns) {
0679             return -1;
0680         }
0681         return index.row() - firstGroupRow();
0682     }
0683 
0684 private:
0685     std::vector<Key> mKeysByFingerprint;
0686     std::vector<KeyGroup> mGroups;
0687 };
0688 
0689 class HierarchicalKeyListModel : public AbstractKeyListModel
0690 {
0691     Q_OBJECT
0692 public:
0693     explicit HierarchicalKeyListModel(QObject *parent = nullptr);
0694     ~HierarchicalKeyListModel() override;
0695 
0696     int rowCount(const QModelIndex &pidx) const override;
0697     using AbstractKeyListModel::index;
0698     QModelIndex index(int row, int col, const QModelIndex &pidx) const override;
0699     QModelIndex parent(const QModelIndex &idx) const override;
0700 
0701     bool hasChildren(const QModelIndex &pidx) const override
0702     {
0703         return rowCount(pidx) > 0;
0704     }
0705 
0706 private:
0707     Key doMapToKey(const QModelIndex &index) const override;
0708     QModelIndex doMapFromKey(const Key &key, int col) const override;
0709     QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
0710     void doRemoveKey(const Key &key) override;
0711 
0712     KeyGroup doMapToGroup(const QModelIndex &index) const override;
0713     QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
0714     void doSetGroups(const std::vector<KeyGroup> &groups) override;
0715     QModelIndex doAddGroup(const KeyGroup &group) override;
0716     bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
0717     bool doRemoveGroup(const KeyGroup &group) override;
0718 
0719     void doClear(ItemTypes types) override;
0720 
0721     int firstGroupRow() const
0722     {
0723         return mTopLevels.size();
0724     }
0725 
0726     int lastGroupRow() const
0727     {
0728         return mTopLevels.size() + mGroups.size() - 1;
0729     }
0730 
0731     int groupIndex(const QModelIndex &index) const
0732     {
0733         if (!index.isValid() || index.row() < firstGroupRow() || index.row() > lastGroupRow() || index.column() >= NumColumns) {
0734             return -1;
0735         }
0736         return index.row() - firstGroupRow();
0737     }
0738 
0739 private:
0740     void addTopLevelKey(const Key &key);
0741     void addKeyWithParent(const char *issuer_fpr, const Key &key);
0742     void addKeyWithoutParent(const char *issuer_fpr, const Key &key);
0743 
0744 private:
0745     typedef std::map<std::string, std::vector<Key>> Map;
0746     std::vector<Key> mKeysByFingerprint; // all keys
0747     Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
0748     std::vector<Key> mTopLevels; // all roots + parent-less
0749     std::vector<KeyGroup> mGroups;
0750 };
0751 
0752 class Issuers
0753 {
0754     Issuers()
0755     {
0756     }
0757 
0758 public:
0759     static Issuers *instance()
0760     {
0761         static auto self = std::unique_ptr<Issuers>{new Issuers{}};
0762         return self.get();
0763     }
0764 
0765     const char *cleanChainID(const Key &key) const
0766     {
0767         const char *chainID = "";
0768         if (!key.isRoot()) {
0769             const char *const chid = key.chainID();
0770             if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
0771                 chainID = chid;
0772             }
0773         }
0774         return chainID;
0775     }
0776 
0777     void maskIssuerOfKey(const Key &key)
0778     {
0779         mKeysWithMaskedIssuer.insert(key);
0780     }
0781 
0782     void clear()
0783     {
0784         mKeysWithMaskedIssuer.clear();
0785     }
0786 
0787 private:
0788     std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
0789 };
0790 
0791 static const char *cleanChainID(const Key &key)
0792 {
0793     return Issuers::instance()->cleanChainID(key);
0794 }
0795 
0796 }
0797 
0798 FlatKeyListModel::FlatKeyListModel(QObject *p)
0799     : TableModelMixin<AbstractKeyListModel>(p)
0800 {
0801 }
0802 
0803 FlatKeyListModel::~FlatKeyListModel()
0804 {
0805 }
0806 
0807 Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const
0808 {
0809     Q_ASSERT(idx.isValid());
0810     if (static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) {
0811         return mKeysByFingerprint[idx.row()];
0812     } else {
0813         return Key::null;
0814     }
0815 }
0816 
0817 QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const
0818 {
0819     Q_ASSERT(!key.isNull());
0820     const std::vector<Key>::const_iterator it =
0821         std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
0822     if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
0823         return {};
0824     } else {
0825         return createIndex(it - mKeysByFingerprint.begin(), col);
0826     }
0827 }
0828 
0829 QList<QModelIndex> FlatKeyListModel::doAddKeys(const std::vector<Key> &keys)
0830 {
0831     Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
0832 
0833     if (keys.empty()) {
0834         return QList<QModelIndex>();
0835     }
0836 
0837     for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
0838         // find an insertion point:
0839         const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
0840         const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
0841 
0842         if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
0843             // key existed before - replace with new one:
0844             mKeysByFingerprint[idx - 1] = *it;
0845             if (!modelResetInProgress()) {
0846                 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
0847             }
0848         } else {
0849             // new key - insert:
0850             if (!modelResetInProgress()) {
0851                 beginInsertRows(QModelIndex(), idx, idx);
0852             }
0853             mKeysByFingerprint.insert(pos, *it);
0854             if (!modelResetInProgress()) {
0855                 endInsertRows();
0856             }
0857         }
0858     }
0859 
0860     return indexes(keys);
0861 }
0862 
0863 void FlatKeyListModel::doRemoveKey(const Key &key)
0864 {
0865     const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
0866     if (it == mKeysByFingerprint.end()) {
0867         return;
0868     }
0869 
0870     const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
0871     if (!modelResetInProgress()) {
0872         beginRemoveRows(QModelIndex(), row, row);
0873     }
0874     mKeysByFingerprint.erase(it);
0875     if (!modelResetInProgress()) {
0876         endRemoveRows();
0877     }
0878 }
0879 
0880 KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const
0881 {
0882     Q_ASSERT(idx.isValid());
0883     if (static_cast<unsigned>(idx.row()) >= mKeysByFingerprint.size() && static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() + mGroups.size()
0884         && idx.column() < NumColumns) {
0885         return mGroups[idx.row() - mKeysByFingerprint.size()];
0886     } else {
0887         return KeyGroup();
0888     }
0889 }
0890 
0891 QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
0892 {
0893     Q_ASSERT(!group.isNull());
0894     const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](const KeyGroup &g) {
0895         return g.source() == group.source() && g.id() == group.id();
0896     });
0897     if (it == mGroups.cend()) {
0898         return QModelIndex();
0899     } else {
0900         return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
0901     }
0902 }
0903 
0904 void FlatKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
0905 {
0906     Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared
0907     const int first = mKeysByFingerprint.size();
0908     const int last = first + groups.size() - 1;
0909     if (!modelResetInProgress()) {
0910         beginInsertRows(QModelIndex(), first, last);
0911     }
0912     mGroups = groups;
0913     if (!modelResetInProgress()) {
0914         endInsertRows();
0915     }
0916 }
0917 
0918 QModelIndex FlatKeyListModel::doAddGroup(const KeyGroup &group)
0919 {
0920     const int newRow = lastGroupRow() + 1;
0921     if (!modelResetInProgress()) {
0922         beginInsertRows(QModelIndex(), newRow, newRow);
0923     }
0924     mGroups.push_back(group);
0925     if (!modelResetInProgress()) {
0926         endInsertRows();
0927     }
0928     return createIndex(newRow, 0);
0929 }
0930 
0931 bool FlatKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
0932 {
0933     if (group.isNull()) {
0934         return false;
0935     }
0936     const int groupIndex = this->groupIndex(index);
0937     if (groupIndex == -1) {
0938         return false;
0939     }
0940     mGroups[groupIndex] = group;
0941     if (!modelResetInProgress()) {
0942         Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
0943     }
0944     return true;
0945 }
0946 
0947 bool FlatKeyListModel::doRemoveGroup(const KeyGroup &group)
0948 {
0949     const QModelIndex modelIndex = doMapFromGroup(group, 0);
0950     if (!modelIndex.isValid()) {
0951         return false;
0952     }
0953     const int groupIndex = this->groupIndex(modelIndex);
0954     Q_ASSERT(groupIndex != -1);
0955     if (groupIndex == -1) {
0956         return false;
0957     }
0958     if (!modelResetInProgress()) {
0959         beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
0960     }
0961     mGroups.erase(mGroups.begin() + groupIndex);
0962     if (!modelResetInProgress()) {
0963         endRemoveRows();
0964     }
0965     return true;
0966 }
0967 
0968 HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p)
0969     : AbstractKeyListModel(p)
0970     , mKeysByFingerprint()
0971     , mKeysByExistingParent()
0972     , mKeysByNonExistingParent()
0973     , mTopLevels()
0974 {
0975 }
0976 
0977 HierarchicalKeyListModel::~HierarchicalKeyListModel()
0978 {
0979 }
0980 
0981 int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const
0982 {
0983     // toplevel item:
0984     if (!pidx.isValid()) {
0985         return mTopLevels.size() + mGroups.size();
0986     }
0987 
0988     if (pidx.column() != 0) {
0989         return 0;
0990     }
0991 
0992     // non-toplevel item - find the number of subjects for this issuer:
0993     const Key issuer = this->key(pidx);
0994     const char *const fpr = issuer.primaryFingerprint();
0995     if (!fpr || !*fpr) {
0996         return 0;
0997     }
0998     const Map::const_iterator it = mKeysByExistingParent.find(fpr);
0999     if (it == mKeysByExistingParent.end()) {
1000         return 0;
1001     }
1002     return it->second.size();
1003 }
1004 
1005 QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const
1006 {
1007     if (row < 0 || col < 0 || col >= NumColumns) {
1008         return {};
1009     }
1010 
1011     // toplevel item:
1012     if (!pidx.isValid()) {
1013         if (static_cast<unsigned>(row) < mTopLevels.size()) {
1014             return index(mTopLevels[row], col);
1015         } else if (static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1016             return index(mGroups[row - mTopLevels.size()], col);
1017         } else {
1018             return QModelIndex();
1019         }
1020     }
1021 
1022     // non-toplevel item - find the row'th subject of this key:
1023     const Key issuer = this->key(pidx);
1024     const char *const fpr = issuer.primaryFingerprint();
1025     if (!fpr || !*fpr) {
1026         return QModelIndex();
1027     }
1028     const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1029     if (it == mKeysByExistingParent.end() || static_cast<unsigned>(row) >= it->second.size()) {
1030         return QModelIndex();
1031     }
1032     return index(it->second[row], col);
1033 }
1034 
1035 QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const
1036 {
1037     const Key key = this->key(idx);
1038     if (key.isNull() || key.isRoot()) {
1039         return {};
1040     }
1041     const std::vector<Key>::const_iterator it =
1042         Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1043     return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex();
1044 }
1045 
1046 Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const
1047 {
1048     Key key = Key::null;
1049 
1050     if (idx.isValid()) {
1051         const char *const issuer_fpr = static_cast<const char *>(idx.internalPointer());
1052         if (!issuer_fpr || !*issuer_fpr) {
1053             // top-level:
1054             if (static_cast<unsigned>(idx.row()) < mTopLevels.size()) {
1055                 key = mTopLevels[idx.row()];
1056             }
1057         } else {
1058             // non-toplevel:
1059             const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1060             if (it != mKeysByExistingParent.end() && static_cast<unsigned>(idx.row()) < it->second.size()) {
1061                 key = it->second[idx.row()];
1062             }
1063         }
1064     }
1065 
1066     return key;
1067 }
1068 
1069 QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const
1070 {
1071     if (key.isNull()) {
1072         return {};
1073     }
1074 
1075     const char *issuer_fpr = cleanChainID(key);
1076 
1077     // we need to look in the toplevels list,...
1078     const std::vector<Key> *v = &mTopLevels;
1079     if (issuer_fpr && *issuer_fpr) {
1080         const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1081         // ...unless we find an existing parent:
1082         if (it != mKeysByExistingParent.end()) {
1083             v = &it->second;
1084         } else {
1085             issuer_fpr = nullptr; // force internalPointer to zero for toplevels
1086         }
1087     }
1088 
1089     const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1090     if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1091         return QModelIndex();
1092     }
1093 
1094     const unsigned int row = std::distance(v->begin(), it);
1095     return createIndex(row, col, const_cast<char * /* thanks, Trolls :/ */>(issuer_fpr));
1096 }
1097 
1098 void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key)
1099 {
1100     Q_ASSERT(issuer_fpr);
1101     Q_ASSERT(*issuer_fpr);
1102     Q_ASSERT(!key.isNull());
1103 
1104     std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1105 
1106     // find insertion point:
1107     const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1108     const int row = std::distance(subjects.begin(), it);
1109 
1110     if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1111         // exists -> replace
1112         *it = key;
1113         if (!modelResetInProgress()) {
1114             Q_EMIT dataChanged(createIndex(row, 0, const_cast<char *>(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast<char *>(issuer_fpr)));
1115         }
1116     } else {
1117         // doesn't exist -> insert
1118         const std::vector<Key>::const_iterator pos =
1119             Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1120         Q_ASSERT(pos != mKeysByFingerprint.end());
1121         if (!modelResetInProgress()) {
1122             beginInsertRows(index(*pos), row, row);
1123         }
1124         subjects.insert(it, key);
1125         if (!modelResetInProgress()) {
1126             endInsertRows();
1127         }
1128     }
1129 }
1130 
1131 void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key)
1132 {
1133     Q_ASSERT(issuer_fpr);
1134     Q_ASSERT(*issuer_fpr);
1135     Q_ASSERT(!key.isNull());
1136 
1137     std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1138 
1139     // find insertion point:
1140     const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1141 
1142     if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1143         // exists -> replace
1144         *it = key;
1145     } else {
1146         // doesn't exist -> insert
1147         subjects.insert(it, key);
1148     }
1149 
1150     addTopLevelKey(key);
1151 }
1152 
1153 void HierarchicalKeyListModel::addTopLevelKey(const Key &key)
1154 {
1155     // find insertion point:
1156     const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1157     const int row = std::distance(mTopLevels.begin(), it);
1158 
1159     if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1160         // exists -> replace
1161         *it = key;
1162         if (!modelResetInProgress()) {
1163             Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1164         }
1165     } else {
1166         // doesn't exist -> insert
1167         if (!modelResetInProgress()) {
1168             beginInsertRows(QModelIndex(), row, row);
1169         }
1170         mTopLevels.insert(it, key);
1171         if (!modelResetInProgress()) {
1172             endInsertRows();
1173         }
1174     }
1175 }
1176 
1177 namespace
1178 {
1179 
1180 // based on https://www.boost.org/doc/libs/1_77_0/libs/graph/doc/file_dependency_example.html#sec:cycles
1181 struct cycle_detector : public boost::dfs_visitor<> {
1182     cycle_detector(bool &has_cycle)
1183         : _has_cycle{has_cycle}
1184     {
1185     }
1186 
1187     template<class Edge, class Graph>
1188     void back_edge(Edge, Graph &)
1189     {
1190         _has_cycle = true;
1191     }
1192 
1193 private:
1194     bool &_has_cycle;
1195 };
1196 
1197 static bool graph_has_cycle(const boost::adjacency_list<> &graph)
1198 {
1199     bool cycle_found = false;
1200     cycle_detector vis{cycle_found};
1201     boost::depth_first_search(graph, visitor(vis));
1202     return cycle_found;
1203 }
1204 
1205 static void find_keys_causing_cycles_and_mask_their_issuers(const std::vector<Key> &keys)
1206 {
1207     boost::adjacency_list<> graph{keys.size()};
1208 
1209     for (unsigned int i = 0, end = keys.size(); i != end; ++i) {
1210         const auto &key = keys[i];
1211         const char *const issuer_fpr = cleanChainID(key);
1212         if (!issuer_fpr || !*issuer_fpr) {
1213             continue;
1214         }
1215         const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1216         if (it == keys.end()) {
1217             continue;
1218         }
1219         const auto j = std::distance(keys.begin(), it);
1220         const auto edge = boost::add_edge(i, j, graph).first;
1221         if (graph_has_cycle(graph)) {
1222             Issuers::instance()->maskIssuerOfKey(key);
1223             boost::remove_edge(edge, graph);
1224         }
1225     }
1226 }
1227 
1228 static auto build_key_graph(const std::vector<Key> &keys)
1229 {
1230     boost::adjacency_list<> graph(keys.size());
1231 
1232     // add edges from children to parents:
1233     for (unsigned int i = 0, end = keys.size(); i != end; ++i) {
1234         const char *const issuer_fpr = cleanChainID(keys[i]);
1235         if (!issuer_fpr || !*issuer_fpr) {
1236             continue;
1237         }
1238         const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1239         if (it == keys.end()) {
1240             continue;
1241         }
1242         const auto j = std::distance(keys.begin(), it);
1243         add_edge(i, j, graph);
1244     }
1245 
1246     return graph;
1247 }
1248 
1249 // sorts 'keys' such that parent always come before their children:
1250 static std::vector<Key> topological_sort(const std::vector<Key> &keys)
1251 {
1252     const auto graph = build_key_graph(keys);
1253 
1254     std::vector<int> order;
1255     order.reserve(keys.size());
1256     topological_sort(graph, std::back_inserter(order));
1257 
1258     Q_ASSERT(order.size() == keys.size());
1259 
1260     std::vector<Key> result;
1261     result.reserve(keys.size());
1262     for (int i : std::as_const(order)) {
1263         result.push_back(keys[i]);
1264     }
1265     return result;
1266 }
1267 
1268 }
1269 
1270 QList<QModelIndex> HierarchicalKeyListModel::doAddKeys(const std::vector<Key> &keys)
1271 {
1272     Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1273 
1274     if (keys.empty()) {
1275         return QList<QModelIndex>();
1276     }
1277 
1278     const std::vector<Key> oldKeys = mKeysByFingerprint;
1279 
1280     std::vector<Key> merged;
1281     merged.reserve(keys.size() + mKeysByFingerprint.size());
1282     std::set_union(keys.begin(),
1283                    keys.end(),
1284                    mKeysByFingerprint.begin(),
1285                    mKeysByFingerprint.end(),
1286                    std::back_inserter(merged),
1287                    _detail::ByFingerprint<std::less>());
1288 
1289     mKeysByFingerprint = merged;
1290 
1291     if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1292         find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1293     }
1294 
1295     std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1296 
1297     const auto topologicalSortedList = topological_sort(keys);
1298     for (const Key &key : topologicalSortedList) {
1299         // check to see whether this key is a parent for a previously parent-less group:
1300         const char *const fpr = key.primaryFingerprint();
1301         if (!fpr || !*fpr) {
1302             continue;
1303         }
1304 
1305         const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1306 
1307         const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1308         const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1309         if (it != mKeysByNonExistingParent.end()) {
1310             mKeysByNonExistingParent.erase(it);
1311         }
1312 
1313         // Step 1: For new keys, remove children from toplevel:
1314 
1315         if (!keyAlreadyExisted) {
1316             auto last = mTopLevels.begin();
1317             auto lastFP = mKeysByFingerprint.begin();
1318 
1319             for (const Key &k : children) {
1320                 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1321                 Q_ASSERT(last != mTopLevels.end());
1322                 const int row = std::distance(mTopLevels.begin(), last);
1323 
1324                 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1325                 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1326 
1327                 Q_EMIT rowAboutToBeMoved(QModelIndex(), row);
1328                 if (!modelResetInProgress()) {
1329                     beginRemoveRows(QModelIndex(), row, row);
1330                 }
1331                 last = mTopLevels.erase(last);
1332                 lastFP = mKeysByFingerprint.erase(lastFP);
1333                 if (!modelResetInProgress()) {
1334                     endRemoveRows();
1335                 }
1336             }
1337         }
1338         // Step 2: add/update key
1339 
1340         const char *const issuer_fpr = cleanChainID(key);
1341         if (!issuer_fpr || !*issuer_fpr) {
1342             // root or something...
1343             addTopLevelKey(key);
1344         } else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1345             // parent exists...
1346             addKeyWithParent(issuer_fpr, key);
1347         } else {
1348             // parent doesn't exist yet...
1349             addKeyWithoutParent(issuer_fpr, key);
1350         }
1351 
1352         const QModelIndex key_idx = index(key);
1353         QModelIndex key_parent = key_idx.parent();
1354         while (key_parent.isValid()) {
1355             changedParents.insert(doMapToKey(key_parent));
1356             key_parent = key_parent.parent();
1357         }
1358 
1359         // Step 3: Add children to new parent ( == key )
1360 
1361         if (!keyAlreadyExisted && !children.empty()) {
1362             addKeys(children);
1363             const QModelIndex new_parent = index(key);
1364             // Q_EMIT the rowMoved() signals in reversed direction, so the
1365             // implementation can use a stack for mapping.
1366             for (int i = children.size() - 1; i >= 0; --i) {
1367                 Q_EMIT rowMoved(new_parent, i);
1368             }
1369         }
1370     }
1371     // Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
1372     // show a parent node if it just got children matching the proxy's filter
1373     if (!modelResetInProgress()) {
1374         for (const Key &i : std::as_const(changedParents)) {
1375             const QModelIndex idx = index(i);
1376             if (idx.isValid()) {
1377                 Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1));
1378             }
1379         }
1380     }
1381     return indexes(keys);
1382 }
1383 
1384 void HierarchicalKeyListModel::doRemoveKey(const Key &key)
1385 {
1386     const QModelIndex idx = index(key);
1387     if (!idx.isValid()) {
1388         return;
1389     }
1390 
1391     const char *const fpr = key.primaryFingerprint();
1392     if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1393         // handle non-leave nodes:
1394         std::vector<Key> keys = mKeysByFingerprint;
1395         const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1396         if (it == keys.end()) {
1397             return;
1398         }
1399         keys.erase(it);
1400         // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
1401         // but acceptable given that deletion of non-leave nodes is rather rare.
1402         clear(Keys);
1403         addKeys(keys);
1404         return;
1405     }
1406 
1407     // handle leave nodes:
1408 
1409     const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1410 
1411     Q_ASSERT(it != mKeysByFingerprint.end());
1412     Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1413     Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1414 
1415     if (!modelResetInProgress()) {
1416         beginRemoveRows(parent(idx), idx.row(), idx.row());
1417     }
1418     mKeysByFingerprint.erase(it);
1419 
1420     const char *const issuer_fpr = cleanChainID(key);
1421 
1422     const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1423     if (tlIt != mTopLevels.end()) {
1424         mTopLevels.erase(tlIt);
1425     }
1426 
1427     if (issuer_fpr && *issuer_fpr) {
1428         const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1429         if (nexIt != mKeysByNonExistingParent.end()) {
1430             const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1431             if (eit != nexIt->second.end()) {
1432                 nexIt->second.erase(eit);
1433             }
1434             if (nexIt->second.empty()) {
1435                 mKeysByNonExistingParent.erase(nexIt);
1436             }
1437         }
1438 
1439         const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1440         if (exIt != mKeysByExistingParent.end()) {
1441             const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1442             if (eit != exIt->second.end()) {
1443                 exIt->second.erase(eit);
1444             }
1445             if (exIt->second.empty()) {
1446                 mKeysByExistingParent.erase(exIt);
1447             }
1448         }
1449     }
1450     if (!modelResetInProgress()) {
1451         endRemoveRows();
1452     }
1453 }
1454 
1455 KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const
1456 {
1457     Q_ASSERT(idx.isValid());
1458     if (idx.parent().isValid()) {
1459         // groups are always top-level
1460         return KeyGroup();
1461     }
1462 
1463     if (static_cast<unsigned>(idx.row()) >= mTopLevels.size() && static_cast<unsigned>(idx.row()) < mTopLevels.size() + mGroups.size()
1464         && idx.column() < NumColumns) {
1465         return mGroups[idx.row() - mTopLevels.size()];
1466     } else {
1467         return KeyGroup();
1468     }
1469 }
1470 
1471 QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
1472 {
1473     Q_ASSERT(!group.isNull());
1474     const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](const KeyGroup &g) {
1475         return g.source() == group.source() && g.id() == group.id();
1476     });
1477     if (it == mGroups.cend()) {
1478         return QModelIndex();
1479     } else {
1480         return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1481     }
1482 }
1483 
1484 void HierarchicalKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
1485 {
1486     Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared
1487     const int first = mTopLevels.size();
1488     const int last = first + groups.size() - 1;
1489     if (!modelResetInProgress()) {
1490         beginInsertRows(QModelIndex(), first, last);
1491     }
1492     mGroups = groups;
1493     if (!modelResetInProgress()) {
1494         endInsertRows();
1495     }
1496 }
1497 
1498 QModelIndex HierarchicalKeyListModel::doAddGroup(const KeyGroup &group)
1499 {
1500     const int newRow = lastGroupRow() + 1;
1501     if (!modelResetInProgress()) {
1502         beginInsertRows(QModelIndex(), newRow, newRow);
1503     }
1504     mGroups.push_back(group);
1505     if (!modelResetInProgress()) {
1506         endInsertRows();
1507     }
1508     return createIndex(newRow, 0);
1509 }
1510 
1511 bool HierarchicalKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
1512 {
1513     if (group.isNull()) {
1514         return false;
1515     }
1516     const int groupIndex = this->groupIndex(index);
1517     if (groupIndex == -1) {
1518         return false;
1519     }
1520     mGroups[groupIndex] = group;
1521     if (!modelResetInProgress()) {
1522         Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
1523     }
1524     return true;
1525 }
1526 
1527 bool HierarchicalKeyListModel::doRemoveGroup(const KeyGroup &group)
1528 {
1529     const QModelIndex modelIndex = doMapFromGroup(group, 0);
1530     if (!modelIndex.isValid()) {
1531         return false;
1532     }
1533     const int groupIndex = this->groupIndex(modelIndex);
1534     Q_ASSERT(groupIndex != -1);
1535     if (groupIndex == -1) {
1536         return false;
1537     }
1538     if (!modelResetInProgress()) {
1539         beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
1540     }
1541     mGroups.erase(mGroups.begin() + groupIndex);
1542     if (!modelResetInProgress()) {
1543         endRemoveRows();
1544     }
1545     return true;
1546 }
1547 
1548 void HierarchicalKeyListModel::doClear(ItemTypes types)
1549 {
1550     if (types & Keys) {
1551         mTopLevels.clear();
1552         mKeysByFingerprint.clear();
1553         mKeysByExistingParent.clear();
1554         mKeysByNonExistingParent.clear();
1555         Issuers::instance()->clear();
1556     }
1557     if (types & Groups) {
1558         mGroups.clear();
1559     }
1560 }
1561 
1562 void AbstractKeyListModel::useKeyCache(bool value, KeyList::Options options)
1563 {
1564     d->m_keyListOptions = options;
1565     d->m_useKeyCache = value;
1566     if (!d->m_useKeyCache) {
1567         clear(All);
1568     } else {
1569         d->updateFromKeyCache();
1570     }
1571     connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this] {
1572         d->updateFromKeyCache();
1573     });
1574 }
1575 
1576 // static
1577 AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p)
1578 {
1579     AbstractKeyListModel *const m = new FlatKeyListModel(p);
1580 #ifdef KLEO_MODEL_TEST
1581     new QAbstractItemModelTester(m, p);
1582 #endif
1583     return m;
1584 }
1585 
1586 // static
1587 AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p)
1588 {
1589     AbstractKeyListModel *const m = new HierarchicalKeyListModel(p);
1590 #ifdef KLEO_MODEL_TEST
1591     new QAbstractItemModelTester(m, p);
1592 #endif
1593     return m;
1594 }
1595 
1596 QMimeData *AbstractKeyListModel::mimeData(const QModelIndexList &indexes) const
1597 {
1598     if (d->m_dragHandler) {
1599         return d->m_dragHandler->mimeData(indexes);
1600     } else {
1601         return QAbstractItemModel::mimeData(indexes);
1602     }
1603 }
1604 
1605 Qt::ItemFlags AbstractKeyListModel::flags(const QModelIndex &index) const
1606 {
1607     if (d->m_dragHandler) {
1608         return d->m_dragHandler->flags(index);
1609     } else {
1610         return QAbstractItemModel::flags(index);
1611     }
1612 }
1613 
1614 QStringList AbstractKeyListModel::mimeTypes() const
1615 {
1616     if (d->m_dragHandler) {
1617         return d->m_dragHandler->mimeTypes();
1618     } else {
1619         return QAbstractItemModel::mimeTypes();
1620     }
1621 }
1622 
1623 void AbstractKeyListModel::setDragHandler(const std::shared_ptr<DragHandler> &dragHandler)
1624 {
1625     d->m_dragHandler = dragHandler;
1626 }
1627 
1628 #include "keylistmodel.moc"
1629 
1630 /*!
1631   \fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
1632 
1633   Emitted before the removal of a row from that model. It will later
1634   be added to the model again, in response to which rowMoved() will be
1635   emitted. If multiple rows are moved in one go, multiple
1636   rowAboutToBeMoved() signals are emitted before the corresponding
1637   number of rowMoved() signals is emitted - in reverse order.
1638 
1639   This works around the absence of move semantics in
1640   QAbstractItemModel. Clients can maintain a stack to perform the
1641   QModelIndex-mapping themselves, or, e.g., to preserve the selection
1642   status of the row:
1643 
1644   \code
1645   std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
1646   // ...
1647   void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
1648       mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
1649   }
1650   void slotRowMoved( const QModelIndex & p, int row ) {
1651       const bool wasSelected = mMovingRowWasSelected.back();
1652       mMovingRowWasSelected.pop_back();
1653       if ( wasSelected )
1654           selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
1655   }
1656   \endcode
1657 
1658   A similar mechanism could be used to preserve the current item during moves.
1659 */
1660 
1661 /*!
1662   \fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
1663 
1664   See rowAboutToBeMoved()
1665 */
1666 
1667 #include "moc_keylistmodel.cpp"