File indexing completed on 2024-04-28 09:46:01

0001 /*
0002     SPDX-FileCopyrightText: 2008, 2009, 2010, 2011, 2012, 2013, 2016, 2017 Rolf Eike Beer <kde@opensource.sf-tec.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 #include "kgpgitemmodel.h"
0007 
0008 #include "gpgproc.h"
0009 #include "kgpgsettings.h"
0010 #include "core/convert.h"
0011 #include "core/images.h"
0012 #include "model/kgpgitemnode.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QLocale>
0017 #include <QMetaObject>
0018 
0019 KGpgItemModel::KGpgItemModel(QObject *parent)
0020     : QAbstractItemModel(parent),
0021     m_root(new KGpgRootNode(this)),
0022     m_default(KGpgSettings::defaultKey())
0023 {
0024     QMetaObject::invokeMethod(this, "refreshGroups", Qt::QueuedConnection);
0025 }
0026 
0027 KGpgItemModel::~KGpgItemModel()
0028 {
0029     delete m_root;
0030 }
0031 
0032 QModelIndex
0033 KGpgItemModel::index(int row, int column, const QModelIndex &parent) const
0034 {
0035     if (hasIndex(row, column, parent)) {
0036         KGpgNode *parentNode = nodeForIndex(parent);
0037         KGpgNode *childNode = parentNode->getChild(row);
0038         return createIndex(row, column, childNode);
0039     }
0040     return QModelIndex();
0041 }
0042 
0043 QModelIndex
0044 KGpgItemModel::parent(const QModelIndex &child) const
0045 {
0046     if (!child.isValid())
0047         return QModelIndex();
0048     KGpgNode *childNode = nodeForIndex(child);
0049     KGpgNode *parentNode = childNode->m_parent;
0050 
0051     if (parentNode == m_root)
0052         return QModelIndex();
0053 
0054     Q_ASSERT(parentNode != nullptr);
0055     int row = rowForNode(parentNode);
0056     int column = 0;
0057     
0058     return createIndex(row, column, parentNode);
0059 }
0060 
0061 int
0062 KGpgItemModel::rowCount(const QModelIndex &parent) const
0063 {
0064     if (parent.column() > 0)
0065         return 0;
0066 
0067     KGpgNode *parentNode = nodeForIndex(parent);
0068 
0069     return parentNode->getChildCount();
0070 }
0071 
0072 bool
0073 KGpgItemModel::hasChildren(const QModelIndex &parent) const
0074 {
0075     if (parent.column() > 0)
0076         return false;
0077 
0078     KGpgNode *parentNode = nodeForIndex(parent);
0079 
0080     return parentNode->hasChildren();
0081 }
0082 
0083 QVariant
0084 KGpgItemModel::data(const QModelIndex &index, int role) const
0085 {
0086     if (!index.isValid())
0087         return QVariant();
0088 
0089     KGpgNode *node = nodeForIndex(index);
0090 
0091     if (role == Qt::FontRole) {
0092         QFont f;
0093         f.setBold(isDefaultKey(node));
0094         return f;
0095     }
0096 
0097     switch (index.column()) {
0098     case KEYCOLUMN_NAME:
0099         switch (role) {
0100         case Qt::DisplayRole:
0101         case Qt::EditRole:
0102             return node->getName();
0103         case Qt::DecorationRole:
0104             switch (node->getType()) {
0105             case ITYPE_GROUP:
0106                 return Images::group();
0107             case ITYPE_GSECRET:
0108             case ITYPE_SECRET:
0109                 return Images::orphan();
0110             case ITYPE_GPUBLIC:
0111             case ITYPE_SUB:
0112             case ITYPE_PUBLIC:
0113                 return Images::single();
0114             case ITYPE_GPAIR:
0115             case ITYPE_PAIR:
0116                 return Images::pair();
0117             case ITYPE_UID:
0118                 return Images::userId();
0119             case ITYPE_UAT:
0120                 return node->toUatNode()->getPixmap();
0121             case ITYPE_REVSIGN:
0122                 return Images::revoke();
0123             case ITYPE_SIGN:
0124                 return Images::signature();
0125             default:
0126                 Q_ASSERT(0);
0127                 return QVariant();
0128             }
0129         case Qt::ToolTipRole:
0130             return node->getComment();
0131         }
0132         break;
0133     case KEYCOLUMN_EMAIL:
0134         if (role == Qt::DisplayRole)
0135             return node->getEmail();
0136         break;
0137     case KEYCOLUMN_TRUST:
0138         {
0139         KgpgKeyTrust t = node->getTrust();
0140         switch(node->getType()) {
0141         case ITYPE_PAIR:
0142         case ITYPE_PUBLIC:
0143             if(!node->toKeyNode()->getKey()->valid())
0144                 t = TRUST_DISABLED;
0145             break;
0146         default:
0147             break;
0148         }
0149 
0150         switch (role) {
0151         case Qt::BackgroundRole:
0152             switch (t) {
0153             case TRUST_INVALID:
0154             case TRUST_DISABLED:
0155                 return KGpgSettings::colorBad();
0156             case TRUST_EXPIRED:
0157                 return KGpgSettings::colorExpired();
0158             case TRUST_MARGINAL:
0159                 return KGpgSettings::colorMarginal();
0160             case TRUST_REVOKED:
0161                 return KGpgSettings::colorRev();
0162             case TRUST_UNDEFINED:
0163             case TRUST_NONE:
0164                 return KGpgSettings::colorUnknown();
0165             case TRUST_FULL:
0166                 return KGpgSettings::colorGood();
0167             case TRUST_ULTIMATE:
0168                 return KGpgSettings::colorUltimate();
0169             case TRUST_UNKNOWN:
0170             default:
0171                 return KGpgSettings::colorUnknown();
0172             }
0173         case Qt::AccessibleTextRole:
0174             return Convert::toString(t);
0175         case Qt::ToolTipRole:
0176             switch(node->getType()) {
0177             case ITYPE_PAIR:
0178             case ITYPE_PUBLIC:
0179                 return i18n("Trust: %1<br>Owner Trust: %2",
0180                         Convert::toString(node->getTrust()),
0181                         Convert::toString(node->toKeyNode()->getKey()->ownerTrust()));
0182             default:
0183                 return i18n("Trust: %1", Convert::toString(node->getTrust()));
0184             }
0185         }
0186         break;
0187         }
0188     case KEYCOLUMN_EXPIR:
0189         if (role == Qt::DisplayRole)
0190             return QLocale().toString(node->getExpiration().date(), QLocale::ShortFormat);
0191         break;
0192     case KEYCOLUMN_SIZE:
0193         switch (role) {
0194         case Qt::DisplayRole:
0195             return node->getSize();
0196         case Qt::ToolTipRole:
0197             switch (node->getType()) {
0198             case ITYPE_PAIR:
0199             case ITYPE_PUBLIC:
0200                 return node->toKeyNode()->getSignCount();
0201             case ITYPE_UAT:
0202                 return node->toUatNode()->getSignCount();
0203             case ITYPE_UID:
0204                 return node->toUidNode()->getSignCount();
0205             case ITYPE_SUB:
0206                 return node->toSubkeyNode()->getSignCount();
0207             }
0208         }
0209         break;
0210     case KEYCOLUMN_CREAT:
0211         if (role == Qt::DisplayRole)
0212             return QLocale().toString(node->getCreation().date(), QLocale::ShortFormat);
0213         break;
0214     case KEYCOLUMN_ID:
0215         switch (role) {
0216         case Qt::DisplayRole:
0217             return node->getId();
0218         case Qt::ToolTipRole:
0219             switch (node->getType()) {
0220             case ITYPE_PAIR:
0221             case ITYPE_PUBLIC:
0222                 return node->toKeyNode()->getFingerprint();
0223             case ITYPE_SECRET:
0224                 return node->toOrphanNode()->getFingerprint();
0225             case ITYPE_SUB:
0226                 return node->toSubkeyNode()->getFingerprint();
0227             default:
0228                 return QVariant();
0229             }
0230         default:
0231             return QVariant();
0232         }
0233         break;
0234     }
0235 
0236     return QVariant();
0237 }
0238 
0239 KGpgNode *
0240 KGpgItemModel::nodeForIndex(const QModelIndex &index) const
0241 {
0242     if (index.isValid())
0243         return static_cast<KGpgNode*>(index.internalPointer());
0244     return m_root;
0245 }
0246 
0247 KGpgKeyNode *
0248 KGpgItemModel::findKeyNode(const QString& id) const
0249 {
0250     return m_root->findKey(id);
0251 }
0252 
0253 int
0254 KGpgItemModel::rowForNode(KGpgNode *node) const
0255 {
0256     return node->m_parent->getChildIndex(node);
0257 }
0258 
0259 KGpgRootNode *
0260 KGpgItemModel::getRootNode() const
0261 {
0262     return m_root;
0263 }
0264 
0265 QString
0266 KGpgItemModel::statusCountMessage() const
0267 {
0268     const int groups = m_root->groupChildren();
0269     const int keys = m_root->getChildCount() - groups;
0270 
0271     return statusCountMessageString(keys, groups);
0272 }
0273 
0274 QString
0275 KGpgItemModel::statusCountMessageString(const unsigned int keys, const unsigned int groups)
0276 {
0277     // Most people will not have groups. Handle this case
0278     // special so the string isn't displayed in this case at all
0279     if (groups == 0) {
0280         return i18np("1 Key", "%1 Keys", keys);
0281     }
0282     
0283     const QString keyString = i18np("1 Key", "%1 Keys", keys);
0284     const QString groupString = i18np("1 Group", "%1 Groups", groups);
0285     
0286     return i18nc("%1 = something like 7 keys, %2 = something like 2 groups", "%1, %2", keyString, groupString);
0287     
0288 }
0289 
0290 KGpgGroupNode *
0291 KGpgItemModel::addGroup(const QString &name, const KGpgKeyNode::List &keys)
0292 {
0293     KGpgGroupNode *nd;
0294     const int cIndex = m_root->getChildCount(); // row of the new node
0295 
0296     beginInsertRows(QModelIndex(), cIndex, cIndex);
0297     nd = new KGpgGroupNode(m_root, name, keys);
0298     endInsertRows();
0299 
0300     nd->saveMembers();
0301 
0302     Q_ASSERT(m_root->getChildIndex(nd) == cIndex);
0303 
0304     return nd;
0305 }
0306 
0307 void
0308 KGpgItemModel::delNode(KGpgNode *node)
0309 {
0310     beginResetModel();
0311     delete node;
0312     endResetModel();
0313 }
0314 
0315 void
0316 KGpgItemModel::changeGroup(KGpgGroupNode *node, const KGpgNode::List &keys)
0317 {
0318     const QModelIndex gIndex = nodeIndex(node);
0319     for (int i = node->getChildCount() - 1; i >= 0; i--) {
0320         bool found = false;
0321 
0322         for (const KGpgNode *nd : keys) {
0323             found = (node->getChild(i)->getId() == nd->getId());
0324             if (found)
0325                 break;
0326         }
0327         if (found)
0328             continue;
0329 
0330         beginRemoveRows(gIndex, i, i);
0331         delete node->getChild(i);
0332         endRemoveRows();
0333     }
0334 
0335     int cnt = node->getChildCount();
0336 
0337     for (int i = 0; i < keys.count(); i++) {
0338         bool found = false;
0339 
0340         for (const KGpgNode *nd : node->getChildren()) {
0341             found = (nd->getId() == keys.at(i)->getId());
0342             if (found)
0343                 break;
0344         }
0345         if (found)
0346             continue;
0347 
0348         beginInsertRows(gIndex, cnt, cnt);
0349         new KGpgGroupMemberNode(node, keys.at(i)->toKeyNode());
0350         endInsertRows();
0351         cnt++;
0352     }
0353 
0354     node->saveMembers();
0355 }
0356 
0357 void
0358 KGpgItemModel::deleteFromGroup(KGpgGroupNode *group, KGpgGroupMemberNode *member)
0359 {
0360     Q_ASSERT(group == member->getParentKeyNode());
0361 
0362     const int childRow = group->getChildIndex(member);
0363     const QModelIndex pIndex = nodeIndex(group);
0364 
0365     beginRemoveRows(pIndex, childRow, childRow);
0366     delete member;
0367     endRemoveRows();
0368 
0369     group->saveMembers();
0370 }
0371 
0372 QVariant
0373 KGpgItemModel::headerData(int section, Qt::Orientation orientation, int role) const
0374 {
0375     if (role != Qt::DisplayRole)
0376         return QVariant();
0377 
0378     if (orientation != Qt::Horizontal)
0379         return QVariant();
0380 
0381     switch (section) {
0382     case KEYCOLUMN_NAME:    return QString(i18n("Name"));
0383     case KEYCOLUMN_EMAIL:   return QString(i18nc("@title:column Title of a column of emails", "Email"));
0384     case KEYCOLUMN_TRUST:   return QString(i18n("Trust"));
0385     case KEYCOLUMN_SIZE:    return QString(i18n("Size"));
0386     case KEYCOLUMN_EXPIR:   return QString(i18n("Expiration"));
0387     case KEYCOLUMN_CREAT:   return QString(i18n("Creation"));
0388     case KEYCOLUMN_ID:  return QString(i18n("ID"));
0389     default:    return QVariant();
0390     }
0391 }
0392 
0393 void
0394 KGpgItemModel::setDefaultKey(KGpgKeyNode *def)
0395 {
0396     int defrow = m_root->findKeyRow(def);
0397     int odefrow = m_root->findKeyRow(m_default);
0398     if (defrow == odefrow)
0399         return;
0400 
0401     int lastcol = columnCount(QModelIndex()) - 1;
0402     if (odefrow >= 0) {
0403         KGpgNode *nd = m_root->getChild(odefrow);
0404         Q_EMIT dataChanged(createIndex(odefrow, 0, nd), createIndex(odefrow, lastcol, nd));
0405     }
0406 
0407     if (def) {
0408         m_default = def->getId();
0409         Q_EMIT dataChanged(createIndex(defrow, 0, def), createIndex(defrow, lastcol, def));
0410     } else {
0411         m_default.clear();
0412     }
0413 }
0414 
0415 QModelIndex
0416 KGpgItemModel::nodeIndex(KGpgNode *node, const int column)
0417 {
0418     KGpgNode *p = node->getParentKeyNode();
0419 
0420     for (int i = 0; i < p->getChildCount(); i++)
0421         if (p->getChild(i) == node)
0422             return createIndex(i, column, node);
0423 
0424     Q_ASSERT(0);
0425     return QModelIndex();
0426 }
0427 
0428 static QStringList
0429 readGroups()
0430 {
0431     return GPGProc::getGgpParsedConfig(KGpgSettings::gpgBinaryPath(), "group");
0432 }
0433 
0434 void
0435 KGpgItemModel::refreshKeys(const QStringList &ids)
0436 {
0437     if (ids.isEmpty()) {
0438         refreshAllKeys();
0439     } else {
0440         beginResetModel();
0441 
0442         QStringList::ConstIterator it = ids.constBegin();
0443         const QStringList::ConstIterator itEnd = ids.constEnd();
0444 
0445         KGpgKeyNode::List refreshNodes;
0446         QStringList addIds;
0447 
0448         for (; it != itEnd; ++it) {
0449             KGpgKeyNode *nd = m_root->findKey(*it);
0450             if (nd)
0451                 refreshNodes << nd;
0452             else
0453                 addIds << *it;
0454         }
0455 
0456         if (!refreshNodes.isEmpty())
0457             m_root->refreshKeys(refreshNodes);
0458         if (!addIds.isEmpty())
0459             m_root->addKeys(addIds);
0460 
0461         endResetModel();
0462     }
0463 }
0464 
0465 void
0466 KGpgItemModel::refreshKeys(KGpgKeyNode::List keys)
0467 {
0468     beginResetModel();
0469     m_root->refreshKeys(keys);
0470     endResetModel();
0471 }
0472 
0473 void
0474 KGpgItemModel::refreshAllKeys()
0475 {
0476     beginResetModel();
0477 
0478     for (int i = m_root->getChildCount() - 1; i >= 0; i--)
0479         delete m_root->getChild(i);
0480     m_root->addKeys();
0481 
0482     m_root->addGroups(readGroups());
0483 
0484     endResetModel();
0485 }
0486 
0487 void
0488 KGpgItemModel::refreshGroups()
0489 {
0490     for (int i = m_root->getChildCount() - 1; i >= 0; i--) {
0491         KGpgNode *nd = m_root->getChild(i);
0492         if (nd->getType() != ITYPE_GROUP)
0493             continue;
0494 
0495         beginRemoveRows(QModelIndex(), i, i);
0496         delete nd;
0497         endRemoveRows();
0498     }
0499 
0500     const QStringList groups = readGroups();
0501 
0502     if (groups.isEmpty())
0503         return;
0504 
0505     const int oldCount = m_root->getChildCount();
0506     beginInsertRows(QModelIndex(), oldCount, oldCount + groups.count());
0507     m_root->addGroups(groups);
0508     endInsertRows();
0509 }
0510 
0511 bool
0512 KGpgItemModel::isDefaultKey(const KGpgNode *node) const
0513 {
0514     return !m_default.isEmpty() && (QStringView(node->getId()).right(m_default.length()).compare(m_default) == 0);
0515 }
0516 
0517 void
0518 KGpgItemModel::invalidateIndexes(KGpgNode *nd)
0519 {
0520     const auto indexList = persistentIndexList();
0521     for (const QModelIndex &idx : indexList) {
0522         KGpgNode *n = nodeForIndex(idx);
0523 
0524         if (n != nd)
0525             continue;
0526 
0527         changePersistentIndex(idx, QModelIndex());
0528     }
0529 }
0530 
0531 void
0532 KGpgItemModel::refreshTrust(const KgpgCore::KgpgKeyTrust trust, const QColor& color)
0533 {
0534     updateNodeTrustColor(m_root, trust, color);
0535 }
0536 
0537 void
0538 KGpgItemModel::updateNodeTrustColor(KGpgExpandableNode *node, const KgpgCore::KgpgKeyTrust trust, const QColor &color)
0539 {
0540     for (int i = 0; i < node->getChildCount(); i++) {
0541         KGpgNode *child = node->getChild(i);
0542 
0543         if (child->getTrust() == trust)
0544             Q_EMIT dataChanged(createIndex(i, KEYCOLUMN_TRUST, child), createIndex(i, KEYCOLUMN_TRUST, child));
0545 
0546         if (!child->hasChildren())
0547             continue;
0548 
0549         KGpgExpandableNode *echild = child->toExpandableNode();
0550         if (echild->wasExpanded())
0551             updateNodeTrustColor(echild, trust, color);
0552     }
0553 }