2021-05-24 07:25:56 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
#include "signaturemodel.h"
|
2022-01-26 16:27:42 +00:00
|
|
|
|
|
|
|
#include "certificatemodel.h"
|
2019-01-05 22:30:25 +00:00
|
|
|
#include "signatureguiutils.h"
|
|
|
|
|
|
|
|
#include <KLocalizedString>
|
|
|
|
|
2022-01-26 16:27:42 +00:00
|
|
|
#include <QFile>
|
2019-01-05 22:30:25 +00:00
|
|
|
#include <QIcon>
|
|
|
|
#include <QPointer>
|
|
|
|
#include <QVector>
|
|
|
|
|
|
|
|
#include "core/document.h"
|
|
|
|
#include "core/form.h"
|
|
|
|
#include "core/observer.h"
|
|
|
|
#include "core/page.h"
|
|
|
|
#include "core/signatureutils.h"
|
|
|
|
|
|
|
|
struct SignatureItem {
|
|
|
|
enum DataType { Root, RevisionInfo, ValidityStatus, SigningTime, Reason, FieldInfo };
|
|
|
|
|
|
|
|
SignatureItem();
|
|
|
|
SignatureItem(SignatureItem *parent, const Okular::FormFieldSignature *form, DataType type, int page);
|
|
|
|
~SignatureItem();
|
|
|
|
|
|
|
|
SignatureItem(const SignatureItem &) = delete;
|
|
|
|
SignatureItem &operator=(const SignatureItem &) = delete;
|
|
|
|
|
|
|
|
QVector<SignatureItem *> children;
|
|
|
|
SignatureItem *parent;
|
|
|
|
const Okular::FormFieldSignature *form;
|
|
|
|
QString displayString;
|
|
|
|
DataType type;
|
|
|
|
int page;
|
|
|
|
};
|
|
|
|
|
|
|
|
SignatureItem::SignatureItem()
|
|
|
|
: parent(nullptr)
|
|
|
|
, form(nullptr)
|
|
|
|
, type(Root)
|
|
|
|
, page(-1)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SignatureItem::SignatureItem(SignatureItem *_parent, const Okular::FormFieldSignature *_form, DataType _type, int _page)
|
|
|
|
: parent(_parent)
|
|
|
|
, form(_form)
|
|
|
|
, type(_type)
|
|
|
|
, page(_page)
|
|
|
|
{
|
|
|
|
Q_ASSERT(parent);
|
|
|
|
parent->children.append(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
SignatureItem::~SignatureItem()
|
|
|
|
{
|
|
|
|
qDeleteAll(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SignatureModelPrivate : public Okular::DocumentObserver
|
|
|
|
{
|
|
|
|
public:
|
2021-08-21 23:14:34 +00:00
|
|
|
explicit SignatureModelPrivate(SignatureModel *qq);
|
2019-01-05 22:30:25 +00:00
|
|
|
~SignatureModelPrivate() override;
|
|
|
|
|
|
|
|
void notifySetup(const QVector<Okular::Page *> &pages, int setupFlags) override;
|
|
|
|
|
|
|
|
QModelIndex indexForItem(SignatureItem *item) const;
|
|
|
|
|
|
|
|
SignatureModel *q;
|
|
|
|
SignatureItem *root;
|
|
|
|
QPointer<Okular::Document> document;
|
2022-01-26 16:27:42 +00:00
|
|
|
mutable QHash<const Okular::FormFieldSignature *, CertificateModel *> certificateForForm;
|
2019-01-05 22:30:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
SignatureModelPrivate::SignatureModelPrivate(SignatureModel *qq)
|
|
|
|
: q(qq)
|
|
|
|
, root(new SignatureItem)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SignatureModelPrivate::~SignatureModelPrivate()
|
|
|
|
{
|
2022-01-26 16:27:42 +00:00
|
|
|
qDeleteAll(certificateForForm);
|
2019-01-05 22:30:25 +00:00
|
|
|
delete root;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateFormFieldSignaturePointer(SignatureItem *item, const QVector<Okular::Page *> &pages)
|
|
|
|
{
|
|
|
|
if (item->form) {
|
2022-03-21 23:59:42 +00:00
|
|
|
const QList<Okular::FormField *> formFields = pages[item->page]->formFields();
|
2020-02-19 16:28:08 +00:00
|
|
|
for (Okular::FormField *f : formFields) {
|
2019-01-05 22:30:25 +00:00
|
|
|
if (item->form->id() == f->id()) {
|
|
|
|
item->form = static_cast<Okular::FormFieldSignature *>(f);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!item->form) {
|
|
|
|
qWarning() << "Lost signature form field, something went wrong";
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 13:39:47 +00:00
|
|
|
for (SignatureItem *child : qAsConst(item->children)) {
|
2019-01-05 22:30:25 +00:00
|
|
|
updateFormFieldSignaturePointer(child, pages);
|
2019-12-09 13:39:47 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SignatureModelPrivate::notifySetup(const QVector<Okular::Page *> &pages, int setupFlags)
|
|
|
|
{
|
|
|
|
if (!(setupFlags & Okular::DocumentObserver::DocumentChanged)) {
|
|
|
|
if (setupFlags & Okular::DocumentObserver::UrlChanged) {
|
|
|
|
updateFormFieldSignaturePointer(root, pages);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->beginResetModel();
|
|
|
|
qDeleteAll(root->children);
|
|
|
|
root->children.clear();
|
|
|
|
|
2022-01-03 15:01:01 +00:00
|
|
|
if (pages.isEmpty()) {
|
|
|
|
q->endResetModel();
|
2022-03-18 21:35:45 +00:00
|
|
|
Q_EMIT q->countChanged();
|
2022-01-03 15:01:01 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int revNumber = 1;
|
2022-01-03 14:49:10 +00:00
|
|
|
int unsignedSignatureNumber = 1;
|
2022-01-03 15:01:01 +00:00
|
|
|
const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(document);
|
|
|
|
for (const Okular::FormFieldSignature *sf : signatureFormFields) {
|
|
|
|
const int pageNumber = sf->page()->number();
|
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
if (sf->signatureType() == Okular::FormFieldSignature::UnsignedSignature) {
|
|
|
|
auto *parentItem = new SignatureItem(root, sf, SignatureItem::RevisionInfo, pageNumber);
|
|
|
|
parentItem->displayString = i18n("Unsigned Signature %1", unsignedSignatureNumber);
|
2022-01-03 15:01:01 +00:00
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
auto childItem = new SignatureItem(parentItem, sf, SignatureItem::FieldInfo, pageNumber);
|
|
|
|
childItem->displayString = i18n("Field: %1 on page %2", sf->name(), pageNumber + 1);
|
2022-01-03 15:01:01 +00:00
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
++unsignedSignatureNumber;
|
|
|
|
} else {
|
|
|
|
const Okular::SignatureInfo &info = sf->signatureInfo();
|
2022-01-03 15:01:01 +00:00
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
// based on whether or not signature form is a nullptr it is decided if clicking on an item should change the viewport.
|
|
|
|
auto *parentItem = new SignatureItem(root, sf, SignatureItem::RevisionInfo, pageNumber);
|
|
|
|
parentItem->displayString = i18n("Rev. %1: Signed By %2", revNumber, info.signerName());
|
2022-01-03 15:01:01 +00:00
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
auto childItem1 = new SignatureItem(parentItem, nullptr, SignatureItem::ValidityStatus, pageNumber);
|
|
|
|
childItem1->displayString = SignatureGuiUtils::getReadableSignatureStatus(info.signatureStatus());
|
2022-01-03 15:01:01 +00:00
|
|
|
|
2022-01-03 14:49:10 +00:00
|
|
|
auto childItem2 = new SignatureItem(parentItem, nullptr, SignatureItem::SigningTime, pageNumber);
|
|
|
|
childItem2->displayString = i18n("Signing Time: %1", info.signingTime().toString(Qt::DefaultLocaleLongDate));
|
|
|
|
|
|
|
|
const QString reason = info.reason();
|
|
|
|
if (!reason.isEmpty()) {
|
|
|
|
auto childItem3 = new SignatureItem(parentItem, nullptr, SignatureItem::Reason, pageNumber);
|
|
|
|
childItem3->displayString = i18n("Reason: %1", reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto childItem4 = new SignatureItem(parentItem, sf, SignatureItem::FieldInfo, pageNumber);
|
|
|
|
childItem4->displayString = i18n("Field: %1 on page %2", sf->name(), pageNumber + 1);
|
|
|
|
|
|
|
|
++revNumber;
|
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
q->endResetModel();
|
2022-03-18 21:35:45 +00:00
|
|
|
Q_EMIT q->countChanged();
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex SignatureModelPrivate::indexForItem(SignatureItem *item) const
|
|
|
|
{
|
|
|
|
if (item->parent) {
|
|
|
|
const int index = item->parent->children.indexOf(item);
|
|
|
|
if (index >= 0 && index < item->parent->children.count()) {
|
|
|
|
return q->createIndex(index, 0, item);
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
SignatureModel::SignatureModel(Okular::Document *doc, QObject *parent)
|
|
|
|
: QAbstractItemModel(parent)
|
|
|
|
, d_ptr(new SignatureModelPrivate(this))
|
|
|
|
{
|
|
|
|
Q_D(SignatureModel);
|
|
|
|
d->document = doc;
|
|
|
|
d->document->addObserver(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
SignatureModel::~SignatureModel()
|
|
|
|
{
|
|
|
|
Q_D(SignatureModel);
|
|
|
|
d->document->removeObserver(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SignatureModel::columnCount(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant SignatureModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
Q_D(const SignatureModel);
|
|
|
|
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return QVariant();
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
const SignatureItem *item = static_cast<SignatureItem *>(index.internalPointer());
|
|
|
|
if (item == d->root) {
|
|
|
|
return QVariant();
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
2022-01-26 16:27:42 +00:00
|
|
|
const Okular::FormFieldSignature *form = item->form ? item->form : item->parent->form;
|
|
|
|
|
2019-01-05 22:30:25 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
case Qt::ToolTipRole:
|
|
|
|
return item->displayString;
|
|
|
|
case Qt::DecorationRole:
|
|
|
|
if (item->type == SignatureItem::RevisionInfo) {
|
2022-01-26 16:27:42 +00:00
|
|
|
const Okular::SignatureInfo::SignatureStatus signatureStatus = form->signatureInfo().signatureStatus();
|
2019-01-05 22:30:25 +00:00
|
|
|
switch (signatureStatus) {
|
|
|
|
case Okular::SignatureInfo::SignatureValid:
|
|
|
|
return QIcon::fromTheme(QStringLiteral("dialog-ok"));
|
|
|
|
case Okular::SignatureInfo::SignatureInvalid:
|
|
|
|
return QIcon::fromTheme(QStringLiteral("dialog-close"));
|
|
|
|
case Okular::SignatureInfo::SignatureDigestMismatch:
|
|
|
|
return QIcon::fromTheme(QStringLiteral("dialog-warning"));
|
|
|
|
default:
|
|
|
|
return QIcon::fromTheme(QStringLiteral("dialog-question"));
|
|
|
|
}
|
2020-07-10 22:15:05 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
return QIcon();
|
|
|
|
case FormRole:
|
2022-01-26 16:27:42 +00:00
|
|
|
return QVariant::fromValue<const Okular::FormFieldSignature *>(form);
|
2019-01-05 22:30:25 +00:00
|
|
|
case PageRole:
|
|
|
|
return item->page;
|
2022-01-26 16:27:42 +00:00
|
|
|
case ReadableStatusRole:
|
|
|
|
return SignatureGuiUtils::getReadableSignatureStatus(form->signatureInfo().signatureStatus());
|
|
|
|
case ReadableModificationSummary:
|
|
|
|
return SignatureGuiUtils::getReadableModificationSummary(form->signatureInfo());
|
|
|
|
case SignerNameRole:
|
|
|
|
return form->signatureInfo().signerName();
|
|
|
|
case SigningTimeRole:
|
|
|
|
return form->signatureInfo().signingTime().toString(Qt::DefaultLocaleLongDate);
|
|
|
|
case SigningLocationRole:
|
|
|
|
return form->signatureInfo().location();
|
|
|
|
case SigningReasonRole:
|
|
|
|
return form->signatureInfo().reason();
|
|
|
|
case CertificateModelRole: {
|
|
|
|
auto it = d->certificateForForm.constFind(form);
|
|
|
|
if (it != d->certificateForForm.constEnd()) {
|
|
|
|
return QVariant::fromValue(it.value());
|
|
|
|
}
|
|
|
|
CertificateModel *cm = new CertificateModel(form->signatureInfo().certificateInfo());
|
|
|
|
d->certificateForForm.insert(form, cm);
|
|
|
|
return QVariant::fromValue(cm);
|
|
|
|
}
|
|
|
|
case SignatureRevisionIndexRole: {
|
|
|
|
const Okular::SignatureInfo &signatureInfo = form->signatureInfo();
|
|
|
|
const Okular::SignatureInfo::SignatureStatus signatureStatus = signatureInfo.signatureStatus();
|
|
|
|
if (signatureStatus != Okular::SignatureInfo::SignatureStatusUnknown && !signatureInfo.signsTotalDocument()) {
|
|
|
|
const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(d->document);
|
|
|
|
return signatureFormFields.indexOf(form);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
case IsUnsignedSignatureRole: {
|
|
|
|
return form->signatureType() == Okular::FormFieldSignature::UnsignedSignature;
|
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SignatureModel::hasChildren(const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
if (!parent.isValid()) {
|
|
|
|
return true;
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
const SignatureItem *item = static_cast<SignatureItem *>(parent.internalPointer());
|
|
|
|
return !item->children.isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex SignatureModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
Q_D(const SignatureModel);
|
|
|
|
|
|
|
|
if (row < 0 || column != 0) {
|
|
|
|
return QModelIndex();
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
const SignatureItem *item = parent.isValid() ? static_cast<SignatureItem *>(parent.internalPointer()) : d->root;
|
|
|
|
if (row < item->children.count()) {
|
|
|
|
return createIndex(row, column, item->children.at(row));
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex SignatureModel::parent(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
Q_D(const SignatureModel);
|
|
|
|
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return QModelIndex();
|
2022-03-08 10:10:43 +00:00
|
|
|
}
|
2019-01-05 22:30:25 +00:00
|
|
|
|
|
|
|
const SignatureItem *item = static_cast<SignatureItem *>(index.internalPointer());
|
|
|
|
return d->indexForItem(item->parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SignatureModel::rowCount(const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
Q_D(const SignatureModel);
|
|
|
|
|
|
|
|
const SignatureItem *item = parent.isValid() ? static_cast<SignatureItem *>(parent.internalPointer()) : d->root;
|
|
|
|
return item->children.count();
|
|
|
|
}
|
|
|
|
|
2022-01-26 16:27:42 +00:00
|
|
|
QHash<int, QByteArray> SignatureModel::roleNames() const
|
|
|
|
{
|
|
|
|
static QHash<int, QByteArray> res;
|
|
|
|
if (res.isEmpty()) {
|
|
|
|
res = QAbstractItemModel::roleNames();
|
|
|
|
res.insert(FormRole, "signatureFormField");
|
|
|
|
res.insert(PageRole, "page");
|
|
|
|
res.insert(ReadableStatusRole, "readableStatus");
|
|
|
|
res.insert(ReadableModificationSummary, "readableModificationSummary");
|
|
|
|
res.insert(SignerNameRole, "signerName");
|
|
|
|
res.insert(SigningTimeRole, "signingTime");
|
|
|
|
res.insert(SigningLocationRole, "signingLocation");
|
|
|
|
res.insert(SigningReasonRole, "signingReason");
|
|
|
|
res.insert(CertificateModelRole, "certificateModel");
|
|
|
|
res.insert(SignatureRevisionIndexRole, "signatureRevisionIndex");
|
|
|
|
res.insert(IsUnsignedSignatureRole, "isUnsignedSignature");
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SignatureModel::saveSignedVersion(int signatureRevisionIndex, const QUrl &filePath) const
|
|
|
|
{
|
|
|
|
Q_D(const SignatureModel);
|
|
|
|
const QVector<const Okular::FormFieldSignature *> signatureFormFields = SignatureGuiUtils::getSignatureFormFields(d->document);
|
|
|
|
if (signatureRevisionIndex < 0 || signatureRevisionIndex >= signatureFormFields.count()) {
|
|
|
|
qWarning() << "Invalid signatureRevisionIndex given to saveSignedVersion";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const Okular::FormFieldSignature *signature = signatureFormFields[signatureRevisionIndex];
|
|
|
|
const QByteArray data = d->document->requestSignedRevisionData(signature->signatureInfo());
|
|
|
|
|
|
|
|
if (!filePath.isLocalFile()) {
|
|
|
|
qWarning() << "Unexpected non local path given to saveSignedVersion" << filePath;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
QFile f(filePath.toLocalFile());
|
|
|
|
if (!f.open(QIODevice::WriteOnly)) {
|
|
|
|
qWarning() << "Failed to open path for writing in saveSignedVersion" << filePath;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (f.write(data) != data.size()) {
|
|
|
|
qWarning() << "Failed to write all data in saveSignedVersion" << filePath;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-05 22:30:25 +00:00
|
|
|
#include "moc_signaturemodel.cpp"
|