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

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     models/useridlistmodel.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2016 Andre Heinecke <aheinecke@gnupg.org>
0007     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0008     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include <config-libkleo.h>
0014 
0015 #include "useridlistmodel.h"
0016 
0017 #include "keycache.h"
0018 
0019 #include <libkleo/formatting.h>
0020 
0021 #include <KLocalizedString>
0022 
0023 #include <QDate>
0024 #include <QIcon>
0025 #include <QVariant>
0026 
0027 #include <gpgme++/key.h>
0028 
0029 using namespace GpgME;
0030 using namespace Kleo;
0031 
0032 class UIDModelItem
0033 {
0034     // A uid model item can either be a UserID::Signature or a UserID.
0035     // you can find out which it is if the uid or the signature return
0036     // null values. (Not null but isNull)
0037     //
0038 public:
0039     explicit UIDModelItem(const UserID::Signature &sig, UIDModelItem *parentItem, bool showRemarks)
0040         : mParentItem{parentItem}
0041         , mSig{sig}
0042     {
0043         const auto name = Formatting::prettyName(sig);
0044         const auto email = Formatting::prettyEMail(sig);
0045         mItemData = {
0046             Formatting::prettyID(sig.signerKeyID()),
0047             name,
0048             email,
0049             Formatting::creationDateString(sig),
0050             Formatting::expirationDateString(sig),
0051             Formatting::validityShort(sig),
0052             sig.isExportable() ? QStringLiteral("✓") : QString{},
0053         };
0054 
0055         QString lastNotation;
0056         if (showRemarks && parentItem) {
0057             for (const auto &notation : sig.notations()) {
0058                 if (notation.name() && !strcmp(notation.name(), "rem@gnupg.org")) {
0059                     lastNotation = QString::fromUtf8(notation.value());
0060                 }
0061             }
0062         }
0063         mItemData.push_back(lastNotation);
0064 
0065         const auto trustSignatureDomain = Formatting::trustSignatureDomain(sig);
0066         mItemData.push_back(trustSignatureDomain);
0067         mAccessibleText = {
0068             Formatting::accessibleHexID(sig.signerKeyID()),
0069             name.isEmpty() ? i18nc("text for screen readers for an empty name", "no name") : QVariant{},
0070             email.isEmpty() ? i18nc("text for screen readers for an empty email address", "no email") : QVariant{},
0071             Formatting::accessibleDate(Formatting::creationDate(sig)),
0072             Formatting::accessibleExpirationDate(sig),
0073             {}, // display text is always okay
0074             sig.isExportable() ? i18nc("yes, is exportable", "yes") : i18nc("no, is not exportable", "no"),
0075             lastNotation.isEmpty() ? i18nc("accessible text for empty list of tags", "none") : QVariant{},
0076             trustSignatureDomain.isEmpty() ? i18n("not applicable") : QVariant{},
0077         };
0078         Q_ASSERT(mAccessibleText.size() == mItemData.size());
0079     }
0080 
0081     explicit UIDModelItem(const UserID &uid, UIDModelItem *parentItem)
0082         : mParentItem{parentItem}
0083         , mUid{uid}
0084     {
0085         mItemData = {Formatting::prettyUserID(uid)};
0086         // for the empty cells of the user ID rows we announce "User ID"
0087         mAccessibleText = {
0088             {}, // use displayed user ID
0089             i18n("User ID"),
0090             i18n("User ID"),
0091             i18n("User ID"),
0092             i18n("User ID"),
0093             i18n("User ID"),
0094             i18n("User ID"),
0095             i18n("User ID"),
0096             i18n("User ID"),
0097         };
0098     }
0099 
0100     // The root item
0101     UIDModelItem()
0102     {
0103         mItemData = {
0104             i18n("User ID / Certification Key ID"),
0105             i18n("Name"),
0106             i18n("E-Mail"),
0107             i18n("Valid From"),
0108             i18n("Valid Until"),
0109             i18n("Status"),
0110             i18n("Exportable"),
0111             i18n("Tags"),
0112             i18n("Trust Signature For"),
0113         };
0114         // mAccessibleText is explicitly left empty
0115     }
0116 
0117     ~UIDModelItem()
0118     {
0119         qDeleteAll(mChildItems);
0120     }
0121 
0122     void appendChild(UIDModelItem *child)
0123     {
0124         mChildItems << child;
0125     }
0126 
0127     UIDModelItem *child(int row) const
0128     {
0129         return mChildItems.value(row);
0130     }
0131 
0132     const UIDModelItem *constChild(int row) const
0133     {
0134         return mChildItems.value(row);
0135     }
0136 
0137     int childCount() const
0138     {
0139         return mChildItems.count();
0140     }
0141 
0142     int columnCount() const
0143     {
0144         if (childCount()) {
0145             // We take the value from the first child
0146             // as we are likely a UID and our children
0147             // are UID Signatures.
0148             return constChild(0)->columnCount();
0149         }
0150         return mItemData.count();
0151     }
0152 
0153     QVariant data(int column) const
0154     {
0155         return mItemData.value(column);
0156     }
0157 
0158     QVariant accessibleText(int column) const
0159     {
0160         return mAccessibleText.value(column);
0161     }
0162 
0163     QVariant toolTip(int column) const
0164     {
0165         if (!mSig.isNull()) {
0166             if (column == static_cast<int>(UserIDListModel::Column::Status)) {
0167                 return i18n("class %1", mSig.certClass());
0168             } else if (column == static_cast<int>(UserIDListModel::Column::TrustSignatureDomain)) {
0169                 return Formatting::trustSignature(mSig);
0170             }
0171         }
0172         return mItemData.value(column);
0173     }
0174 
0175     QVariant icon(int column) const
0176     {
0177         if (!mSig.isNull() && column == static_cast<int>(UserIDListModel::Column::Status)) {
0178             return Formatting::validityIcon(mSig);
0179         }
0180         return {};
0181     }
0182 
0183     int row() const
0184     {
0185         if (mParentItem) {
0186             return mParentItem->mChildItems.indexOf(const_cast<UIDModelItem *>(this));
0187         }
0188         return 0;
0189     }
0190 
0191     UIDModelItem *parentItem() const
0192     {
0193         return mParentItem;
0194     }
0195 
0196     UserID::Signature signature() const
0197     {
0198         return mSig;
0199     }
0200 
0201     UserID uid() const
0202     {
0203         return mUid;
0204     }
0205 
0206 private:
0207     QList<UIDModelItem *> mChildItems;
0208     QList<QVariant> mItemData;
0209     QList<QVariant> mAccessibleText;
0210     UIDModelItem *mParentItem = nullptr;
0211     UserID::Signature mSig;
0212     UserID mUid;
0213 };
0214 
0215 UserIDListModel::UserIDListModel(QObject *p)
0216     : QAbstractItemModel{p}
0217 {
0218 }
0219 
0220 UserIDListModel::~UserIDListModel() = default;
0221 
0222 Key UserIDListModel::key() const
0223 {
0224     return mKey;
0225 }
0226 
0227 void UserIDListModel::setKey(const Key &key)
0228 {
0229     beginResetModel();
0230     mKey = key;
0231 
0232     mRootItem.reset(new UIDModelItem);
0233     for (int i = 0, ids = key.numUserIDs(); i < ids; ++i) {
0234         UserID uid = key.userID(i);
0235         auto uidItem = new UIDModelItem(uid, mRootItem.get());
0236         mRootItem->appendChild(uidItem);
0237         std::vector<UserID::Signature> sigs = uid.signatures();
0238         std::sort(sigs.begin(), sigs.end());
0239         for (const auto &sig : sigs) {
0240             auto sigItem = new UIDModelItem(sig, uidItem, mRemarksEnabled);
0241             uidItem->appendChild(sigItem);
0242         }
0243     }
0244 
0245     endResetModel();
0246 }
0247 
0248 int UserIDListModel::columnCount(const QModelIndex &parent) const
0249 {
0250     if (parent.isValid()) {
0251         return static_cast<UIDModelItem *>(parent.internalPointer())->columnCount();
0252     }
0253 
0254     if (!mRootItem) {
0255         return 0;
0256     }
0257 
0258     return mRootItem->columnCount();
0259 }
0260 
0261 int UserIDListModel::rowCount(const QModelIndex &parent) const
0262 {
0263     if (parent.column() > 0 || !mRootItem) {
0264         return 0;
0265     }
0266 
0267     const UIDModelItem *const parentItem = !parent.isValid() ? mRootItem.get() : static_cast<UIDModelItem *>(parent.internalPointer());
0268     return parentItem->childCount();
0269 }
0270 
0271 QModelIndex UserIDListModel::index(int row, int column, const QModelIndex &parent) const
0272 {
0273     if (!hasIndex(row, column, parent)) {
0274         return {};
0275     }
0276 
0277     const UIDModelItem *const parentItem = !parent.isValid() ? mRootItem.get() : static_cast<UIDModelItem *>(parent.internalPointer());
0278     UIDModelItem *const childItem = parentItem->child(row);
0279     if (childItem) {
0280         return createIndex(row, column, childItem);
0281     } else {
0282         return QModelIndex();
0283     }
0284 }
0285 
0286 QModelIndex UserIDListModel::parent(const QModelIndex &index) const
0287 {
0288     if (!index.isValid()) {
0289         return {};
0290     }
0291     auto childItem = static_cast<UIDModelItem *>(index.internalPointer());
0292     UIDModelItem *parentItem = childItem->parentItem();
0293 
0294     if (parentItem == mRootItem.get()) {
0295         return QModelIndex();
0296     }
0297 
0298     return createIndex(parentItem->row(), 0, parentItem);
0299 }
0300 
0301 QVariant UserIDListModel::headerData(int section, Qt::Orientation o, int role) const
0302 {
0303     if (o == Qt::Horizontal && mRootItem) {
0304         if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
0305             return mRootItem->data(section);
0306         } else if (role == Qt::AccessibleTextRole) {
0307             return mRootItem->accessibleText(section);
0308         }
0309     }
0310     return QVariant();
0311 }
0312 
0313 QVariant UserIDListModel::data(const QModelIndex &index, int role) const
0314 {
0315     if (!index.isValid()) {
0316         return QVariant();
0317     }
0318 
0319     auto item = static_cast<UIDModelItem *>(index.internalPointer());
0320 
0321     switch (role) {
0322     case Qt::DisplayRole:
0323     case Qt::EditRole:
0324         return item->data(index.column());
0325     case Qt::AccessibleTextRole:
0326         return item->accessibleText(index.column());
0327     case Qt::ToolTipRole:
0328         return item->toolTip(index.column());
0329     case Qt::DecorationRole:
0330         return item->icon(index.column());
0331     default:;
0332     }
0333 
0334     return {};
0335 }
0336 
0337 UserID UserIDListModel::userID(const QModelIndex &index) const
0338 {
0339     if (!index.isValid()) {
0340         return UserID();
0341     }
0342     UIDModelItem *item = static_cast<UIDModelItem *>(index.internalPointer());
0343     return item->uid();
0344 }
0345 
0346 QList<UserID> UserIDListModel::userIDs(const QModelIndexList &indexes) const
0347 {
0348     QList<GpgME::UserID> ret;
0349     for (const QModelIndex &idx : indexes) {
0350         if (!idx.isValid()) {
0351             continue;
0352         }
0353         auto item = static_cast<UIDModelItem *>(idx.internalPointer());
0354         if (!item->uid().isNull()) {
0355             ret << item->uid();
0356         }
0357     }
0358     return ret;
0359 }
0360 
0361 UserID::Signature UserIDListModel::signature(const QModelIndex &index) const
0362 {
0363     if (!index.isValid()) {
0364         return UserID::Signature();
0365     }
0366     UIDModelItem *item = static_cast<UIDModelItem *>(index.internalPointer());
0367     return item->signature();
0368 }
0369 
0370 QList<UserID::Signature> UserIDListModel::signatures(const QModelIndexList &indexes) const
0371 {
0372     QList<GpgME::UserID::Signature> ret;
0373     for (const QModelIndex &idx : indexes) {
0374         if (!idx.isValid()) {
0375             continue;
0376         }
0377         auto item = static_cast<UIDModelItem *>(idx.internalPointer());
0378         if (!item->signature().isNull()) {
0379             ret << item->signature();
0380         }
0381     }
0382     return ret;
0383 }
0384 
0385 void UserIDListModel::enableRemarks(bool value)
0386 {
0387     mRemarksEnabled = value;
0388 }
0389 
0390 #include "moc_useridlistmodel.cpp"