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"