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"