File indexing completed on 2024-04-28 04:33:07

0001 /*
0002     SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "signaturemodel.h"
0008 
0009 #include "certificatemodel.h"
0010 #include "signatureguiutils.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QFile>
0015 #include <QIcon>
0016 #include <QPointer>
0017 #include <QVector>
0018 
0019 #include "core/document.h"
0020 #include "core/form.h"
0021 #include "core/observer.h"
0022 #include "core/page.h"
0023 #include "core/signatureutils.h"
0024 
0025 struct SignatureItem {
0026     enum DataType { Root, RevisionInfo, ValidityStatus, SigningTime, Reason, Location, FieldInfo };
0027 
0028     SignatureItem();
0029     SignatureItem(SignatureItem *parent, const Okular::FormFieldSignature *form, DataType type, int page);
0030     ~SignatureItem();
0031 
0032     SignatureItem(const SignatureItem &) = delete;
0033     SignatureItem &operator=(const SignatureItem &) = delete;
0034 
0035     QVector<SignatureItem *> children;
0036     SignatureItem *parent;
0037     const Okular::FormFieldSignature *form;
0038     QString displayString;
0039     DataType type;
0040     int page;
0041 };
0042 
0043 SignatureItem::SignatureItem()
0044     : parent(nullptr)
0045     , form(nullptr)
0046     , type(Root)
0047     , page(-1)
0048 {
0049 }
0050 
0051 SignatureItem::SignatureItem(SignatureItem *_parent, const Okular::FormFieldSignature *_form, DataType _type, int _page)
0052     : parent(_parent)
0053     , form(_form)
0054     , type(_type)
0055     , page(_page)
0056 {
0057     Q_ASSERT(parent);
0058     parent->children.append(this);
0059 }
0060 
0061 SignatureItem::~SignatureItem()
0062 {
0063     qDeleteAll(children);
0064 }
0065 
0066 class SignatureModelPrivate : public Okular::DocumentObserver
0067 {
0068 public:
0069     explicit SignatureModelPrivate(SignatureModel *qq);
0070     ~SignatureModelPrivate() override;
0071 
0072     void notifySetup(const QVector<Okular::Page *> &pages, int setupFlags) override;
0073 
0074     QModelIndex indexForItem(SignatureItem *item) const;
0075 
0076     SignatureModel *q;
0077     SignatureItem *root;
0078     QPointer<Okular::Document> document;
0079     mutable QHash<const Okular::FormFieldSignature *, CertificateModel *> certificateForForm;
0080 };
0081 
0082 SignatureModelPrivate::SignatureModelPrivate(SignatureModel *qq)
0083     : q(qq)
0084     , root(new SignatureItem)
0085 {
0086 }
0087 
0088 SignatureModelPrivate::~SignatureModelPrivate()
0089 {
0090     qDeleteAll(certificateForForm);
0091     delete root;
0092 }
0093 
0094 static void updateFormFieldSignaturePointer(SignatureItem *item, const QVector<Okular::Page *> &pages)
0095 {
0096     if (item->form) {
0097         const QList<Okular::FormField *> formFields = pages[item->page]->formFields();
0098         for (Okular::FormField *f : formFields) {
0099             if (item->form->id() == f->id()) {
0100                 item->form = static_cast<Okular::FormFieldSignature *>(f);
0101                 break;
0102             }
0103         }
0104         if (!item->form) {
0105             qWarning() << "Lost signature form field, something went wrong";
0106         }
0107     }
0108 
0109     for (SignatureItem *child : std::as_const(item->children)) {
0110         updateFormFieldSignaturePointer(child, pages);
0111     }
0112 }
0113 
0114 void SignatureModelPrivate::notifySetup(const QVector<Okular::Page *> &pages, int setupFlags)
0115 {
0116     if (!(setupFlags & Okular::DocumentObserver::DocumentChanged)) {
0117         if (setupFlags & Okular::DocumentObserver::UrlChanged) {
0118             updateFormFieldSignaturePointer(root, pages);
0119         }
0120         return;
0121     }
0122 
0123     q->beginResetModel();
0124     qDeleteAll(root->children);
0125     root->children.clear();
0126 
0127     if (pages.isEmpty()) {
0128         q->endResetModel();
0129         Q_EMIT q->countChanged();
0130         return;
0131     }
0132 
0133     int revNumber = 1;
0134     int unsignedSignatureNumber = 1;
0135     const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(document);
0136     for (const Okular::FormFieldSignature *sf : signatureFormFields) {
0137         const int pageNumber = sf->page()->number();
0138 
0139         if (sf->signatureType() == Okular::FormFieldSignature::UnsignedSignature) {
0140             auto *parentItem = new SignatureItem(root, sf, SignatureItem::RevisionInfo, pageNumber);
0141             parentItem->displayString = i18n("Unsigned Signature %1", unsignedSignatureNumber);
0142 
0143             auto childItem = new SignatureItem(parentItem, sf, SignatureItem::FieldInfo, pageNumber);
0144             childItem->displayString = i18n("Field: %1 on page %2", sf->name(), pageNumber + 1);
0145 
0146             ++unsignedSignatureNumber;
0147         } else {
0148             const Okular::SignatureInfo &info = sf->signatureInfo();
0149 
0150             // based on whether or not signature form is a nullptr it is decided if clicking on an item should change the viewport.
0151             auto *parentItem = new SignatureItem(root, sf, SignatureItem::RevisionInfo, pageNumber);
0152             parentItem->displayString = i18n("Rev. %1: Signed By %2", revNumber, info.signerName());
0153 
0154             auto childItem1 = new SignatureItem(parentItem, nullptr, SignatureItem::ValidityStatus, pageNumber);
0155             childItem1->displayString = SignatureGuiUtils::getReadableSignatureStatus(info.signatureStatus());
0156 
0157             auto childItem2 = new SignatureItem(parentItem, nullptr, SignatureItem::SigningTime, pageNumber);
0158             childItem2->displayString = i18n("Signing Time: %1", QLocale().toString(info.signingTime(), QLocale::LongFormat));
0159 
0160             const QString reason = info.reason();
0161             if (!reason.isEmpty()) {
0162                 auto childItem3 = new SignatureItem(parentItem, nullptr, SignatureItem::Reason, pageNumber);
0163                 childItem3->displayString = i18n("Reason: %1", reason);
0164             }
0165             const QString location = info.location();
0166             if (!location.isEmpty()) {
0167                 auto childItem3 = new SignatureItem(parentItem, nullptr, SignatureItem::Location, pageNumber);
0168                 childItem3->displayString = i18n("Location: %1", location);
0169             }
0170 
0171             auto childItem4 = new SignatureItem(parentItem, sf, SignatureItem::FieldInfo, pageNumber);
0172             childItem4->displayString = i18n("Field: %1 on page %2", sf->name(), pageNumber + 1);
0173 
0174             ++revNumber;
0175         }
0176     }
0177     q->endResetModel();
0178     Q_EMIT q->countChanged();
0179 }
0180 
0181 QModelIndex SignatureModelPrivate::indexForItem(SignatureItem *item) const
0182 {
0183     if (item->parent) {
0184         const int index = item->parent->children.indexOf(item);
0185         if (index >= 0 && index < item->parent->children.count()) {
0186             return q->createIndex(index, 0, item);
0187         }
0188     }
0189     return QModelIndex();
0190 }
0191 
0192 SignatureModel::SignatureModel(Okular::Document *doc, QObject *parent)
0193     : QAbstractItemModel(parent)
0194     , d_ptr(new SignatureModelPrivate(this))
0195 {
0196     Q_D(SignatureModel);
0197     d->document = doc;
0198     d->document->addObserver(d);
0199 }
0200 
0201 SignatureModel::~SignatureModel()
0202 {
0203     Q_D(SignatureModel);
0204     d->document->removeObserver(d);
0205 }
0206 
0207 int SignatureModel::columnCount(const QModelIndex &) const
0208 {
0209     return 1;
0210 }
0211 
0212 QVariant SignatureModel::data(const QModelIndex &index, int role) const
0213 {
0214     Q_D(const SignatureModel);
0215 
0216     if (!index.isValid()) {
0217         return QVariant();
0218     }
0219 
0220     const SignatureItem *item = static_cast<SignatureItem *>(index.internalPointer());
0221     if (item == d->root) {
0222         return QVariant();
0223     }
0224 
0225     const Okular::FormFieldSignature *form = item->form ? item->form : item->parent->form;
0226 
0227     switch (role) {
0228     case Qt::DisplayRole:
0229     case Qt::ToolTipRole:
0230         return item->displayString;
0231     case Qt::DecorationRole:
0232         if (item->type == SignatureItem::RevisionInfo) {
0233             const Okular::SignatureInfo::SignatureStatus signatureStatus = form->signatureInfo().signatureStatus();
0234             switch (signatureStatus) {
0235             case Okular::SignatureInfo::SignatureValid:
0236                 return QIcon::fromTheme(QStringLiteral("dialog-ok"));
0237             case Okular::SignatureInfo::SignatureInvalid:
0238                 return QIcon::fromTheme(QStringLiteral("dialog-close"));
0239             case Okular::SignatureInfo::SignatureDigestMismatch:
0240                 return QIcon::fromTheme(QStringLiteral("dialog-warning"));
0241             default:
0242                 return QIcon::fromTheme(QStringLiteral("dialog-question"));
0243             }
0244         }
0245         return QIcon();
0246     case FormRole:
0247         return QVariant::fromValue<const Okular::FormFieldSignature *>(form);
0248     case PageRole:
0249         return item->page;
0250     case ReadableStatusRole:
0251         return SignatureGuiUtils::getReadableSignatureStatus(form->signatureInfo().signatureStatus());
0252     case ReadableModificationSummary:
0253         return SignatureGuiUtils::getReadableModificationSummary(form->signatureInfo());
0254     case SignerNameRole:
0255         return form->signatureInfo().signerName();
0256     case SigningTimeRole:
0257         return QLocale().toString(form->signatureInfo().signingTime(), QLocale::LongFormat);
0258     case SigningLocationRole:
0259         return form->signatureInfo().location();
0260     case SigningReasonRole:
0261         return form->signatureInfo().reason();
0262     case CertificateModelRole: {
0263         auto it = d->certificateForForm.constFind(form);
0264         if (it != d->certificateForForm.constEnd()) {
0265             return QVariant::fromValue(it.value());
0266         }
0267         CertificateModel *cm = new CertificateModel(form->signatureInfo().certificateInfo());
0268         d->certificateForForm.insert(form, cm);
0269         return QVariant::fromValue(cm);
0270     }
0271     case SignatureRevisionIndexRole: {
0272         const Okular::SignatureInfo &signatureInfo = form->signatureInfo();
0273         const Okular::SignatureInfo::SignatureStatus signatureStatus = signatureInfo.signatureStatus();
0274         if (signatureStatus != Okular::SignatureInfo::SignatureStatusUnknown && !signatureInfo.signsTotalDocument()) {
0275             const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(d->document);
0276             return signatureFormFields.indexOf(form);
0277         }
0278         return -1;
0279     }
0280     case IsUnsignedSignatureRole: {
0281         return form->signatureType() == Okular::FormFieldSignature::UnsignedSignature;
0282     }
0283     }
0284 
0285     return QVariant();
0286 }
0287 
0288 bool SignatureModel::hasChildren(const QModelIndex &parent) const
0289 {
0290     if (!parent.isValid()) {
0291         return true;
0292     }
0293 
0294     const SignatureItem *item = static_cast<SignatureItem *>(parent.internalPointer());
0295     return !item->children.isEmpty();
0296 }
0297 
0298 QModelIndex SignatureModel::index(int row, int column, const QModelIndex &parent) const
0299 {
0300     Q_D(const SignatureModel);
0301 
0302     if (row < 0 || column != 0) {
0303         return QModelIndex();
0304     }
0305 
0306     const SignatureItem *item = parent.isValid() ? static_cast<SignatureItem *>(parent.internalPointer()) : d->root;
0307     if (row < item->children.count()) {
0308         return createIndex(row, column, item->children.at(row));
0309     }
0310 
0311     return QModelIndex();
0312 }
0313 
0314 QModelIndex SignatureModel::parent(const QModelIndex &index) const
0315 {
0316     Q_D(const SignatureModel);
0317 
0318     if (!index.isValid()) {
0319         return QModelIndex();
0320     }
0321 
0322     const SignatureItem *item = static_cast<SignatureItem *>(index.internalPointer());
0323     return d->indexForItem(item->parent);
0324 }
0325 
0326 int SignatureModel::rowCount(const QModelIndex &parent) const
0327 {
0328     Q_D(const SignatureModel);
0329 
0330     const SignatureItem *item = parent.isValid() ? static_cast<SignatureItem *>(parent.internalPointer()) : d->root;
0331     return item->children.count();
0332 }
0333 
0334 QHash<int, QByteArray> SignatureModel::roleNames() const
0335 {
0336     static QHash<int, QByteArray> res;
0337     if (res.isEmpty()) {
0338         res = QAbstractItemModel::roleNames();
0339         res.insert(FormRole, "signatureFormField");
0340         res.insert(PageRole, "page");
0341         res.insert(ReadableStatusRole, "readableStatus");
0342         res.insert(ReadableModificationSummary, "readableModificationSummary");
0343         res.insert(SignerNameRole, "signerName");
0344         res.insert(SigningTimeRole, "signingTime");
0345         res.insert(SigningLocationRole, "signingLocation");
0346         res.insert(SigningReasonRole, "signingReason");
0347         res.insert(CertificateModelRole, "certificateModel");
0348         res.insert(SignatureRevisionIndexRole, "signatureRevisionIndex");
0349         res.insert(IsUnsignedSignatureRole, "isUnsignedSignature");
0350     }
0351 
0352     return res;
0353 }
0354 
0355 bool SignatureModel::saveSignedVersion(int signatureRevisionIndex, const QUrl &filePath) const
0356 {
0357     Q_D(const SignatureModel);
0358     const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(d->document);
0359     if (signatureRevisionIndex < 0 || signatureRevisionIndex >= signatureFormFields.count()) {
0360         qWarning() << "Invalid signatureRevisionIndex given to saveSignedVersion";
0361         return false;
0362     }
0363     const Okular::FormFieldSignature *signature = signatureFormFields[signatureRevisionIndex];
0364     const QByteArray data = d->document->requestSignedRevisionData(signature->signatureInfo());
0365 
0366     if (!filePath.isLocalFile()) {
0367         qWarning() << "Unexpected non local path given to saveSignedVersion" << filePath;
0368         return false;
0369     }
0370     QFile f(filePath.toLocalFile());
0371     if (!f.open(QIODevice::WriteOnly)) {
0372         qWarning() << "Failed to open path for writing in saveSignedVersion" << filePath;
0373         return false;
0374     }
0375     if (f.write(data) != data.size()) {
0376         qWarning() << "Failed to write all data in saveSignedVersion" << filePath;
0377         return false;
0378     }
0379     return true;
0380 }
0381 
0382 #include "moc_signaturemodel.cpp"