Move a few files from part/ to gui/ since they are also used by the mobile/ frontendremotes/origin/work/fhek/foreground_background_text
parent
28e362b6a1
commit
4cd6bfd30e
56 changed files with 1104 additions and 365 deletions
@ -0,0 +1,163 @@ |
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> |
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
#include "certificatemodel.h" |
||||
|
||||
#include "signatureguiutils.h" |
||||
|
||||
#include <KLocalizedString> |
||||
|
||||
#include <QCryptographicHash> |
||||
#include <QDebug> |
||||
#include <QFile> |
||||
#include <QUrl> |
||||
|
||||
CertificateModel::CertificateModel(const Okular::CertificateInfo &certInfo, QObject *parent) |
||||
: QAbstractTableModel(parent) |
||||
, m_certificateInfo(certInfo) |
||||
{ |
||||
m_certificateProperties = {Version, SerialNumber, Issuer, IssuedOn, ExpiresOn, Subject, PublicKey, KeyUsage}; |
||||
} |
||||
|
||||
int CertificateModel::columnCount(const QModelIndex &) const |
||||
{ |
||||
return 2; |
||||
} |
||||
|
||||
int CertificateModel::rowCount(const QModelIndex &) const |
||||
{ |
||||
return m_certificateProperties.size(); |
||||
} |
||||
|
||||
static QString propertyVisibleName(CertificateModel::Property p) |
||||
{ |
||||
switch (p) { |
||||
case CertificateModel::Version: |
||||
return i18n("Version"); |
||||
case CertificateModel::SerialNumber: |
||||
return i18n("Serial Number"); |
||||
case CertificateModel::Issuer: |
||||
return i18n("Issuer"); |
||||
case CertificateModel::IssuedOn: |
||||
return i18n("Issued On"); |
||||
case CertificateModel::ExpiresOn: |
||||
return i18n("Expires On"); |
||||
case CertificateModel::Subject: |
||||
return i18nc("The person/company that made the signature", "Subject"); |
||||
case CertificateModel::PublicKey: |
||||
return i18n("Public Key"); |
||||
case CertificateModel::KeyUsage: |
||||
return i18n("Key Usage"); |
||||
case CertificateModel::IssuerName: |
||||
case CertificateModel::IssuerEmail: |
||||
case CertificateModel::IssuerOrganization: |
||||
case CertificateModel::SubjectName: |
||||
case CertificateModel::SubjectEmail: |
||||
case CertificateModel::SubjectOrganization: |
||||
case CertificateModel::Sha1: |
||||
case CertificateModel::Sha256: |
||||
Q_ASSERT(false); |
||||
qWarning() << "Unimplemented"; |
||||
} |
||||
return QString(); |
||||
} |
||||
|
||||
QString CertificateModel::propertyVisibleValue(CertificateModel::Property p) const |
||||
{ |
||||
switch (p) { |
||||
case CertificateModel::Version: |
||||
return i18n("V%1", QString::number(m_certificateInfo.version())); |
||||
case CertificateModel::SerialNumber: |
||||
return m_certificateInfo.serialNumber().toHex(' '); |
||||
case CertificateModel::Issuer: |
||||
return m_certificateInfo.issuerInfo(Okular::CertificateInfo::DistinguishedName); |
||||
case CertificateModel::IssuedOn: |
||||
return m_certificateInfo.validityStart().toString(Qt::DefaultLocaleLongDate); |
||||
case CertificateModel::ExpiresOn: |
||||
return m_certificateInfo.validityEnd().toString(Qt::DefaultLocaleLongDate); |
||||
case CertificateModel::Subject: |
||||
return m_certificateInfo.subjectInfo(Okular::CertificateInfo::DistinguishedName); |
||||
case CertificateModel::PublicKey: |
||||
return i18n("%1 (%2 bits)", SignatureGuiUtils::getReadablePublicKeyType(m_certificateInfo.publicKeyType()), m_certificateInfo.publicKeyStrength()); |
||||
case CertificateModel::KeyUsage: |
||||
return SignatureGuiUtils::getReadableKeyUsageCommaSeparated(m_certificateInfo.keyUsageExtensions()); |
||||
case CertificateModel::IssuerName: |
||||
return m_certificateInfo.issuerInfo(Okular::CertificateInfo::CommonName); |
||||
case CertificateModel::IssuerEmail: |
||||
return m_certificateInfo.issuerInfo(Okular::CertificateInfo::EmailAddress); |
||||
case CertificateModel::IssuerOrganization: |
||||
return m_certificateInfo.issuerInfo(Okular::CertificateInfo::Organization); |
||||
case CertificateModel::SubjectName: |
||||
return m_certificateInfo.subjectInfo(Okular::CertificateInfo::CommonName); |
||||
case CertificateModel::SubjectEmail: |
||||
return m_certificateInfo.subjectInfo(Okular::CertificateInfo::EmailAddress); |
||||
case CertificateModel::SubjectOrganization: |
||||
return m_certificateInfo.subjectInfo(Okular::CertificateInfo::Organization); |
||||
case CertificateModel::Sha1: |
||||
return QCryptographicHash::hash(m_certificateInfo.certificateData(), QCryptographicHash::Sha1).toHex(' '); |
||||
case CertificateModel::Sha256: |
||||
return QCryptographicHash::hash(m_certificateInfo.certificateData(), QCryptographicHash::Sha256).toHex(' '); |
||||
} |
||||
return QString(); |
||||
} |
||||
|
||||
QVariant CertificateModel::data(const QModelIndex &index, int role) const |
||||
{ |
||||
const int row = index.row(); |
||||
if (!index.isValid() || row < 0 || row >= m_certificateProperties.count()) |
||||
return QVariant(); |
||||
|
||||
switch (role) { |
||||
case Qt::DisplayRole: |
||||
case Qt::ToolTipRole: |
||||
switch (index.column()) { |
||||
case 0: |
||||
return propertyVisibleName(m_certificateProperties[row]); |
||||
case 1: |
||||
return propertyVisibleValue(m_certificateProperties[row]); |
||||
default: |
||||
return QString(); |
||||
} |
||||
case PropertyKeyRole: |
||||
return m_certificateProperties[row]; |
||||
case PropertyVisibleValueRole: |
||||
return propertyVisibleValue(m_certificateProperties[row]); |
||||
} |
||||
|
||||
return QVariant(); |
||||
} |
||||
|
||||
QVariant CertificateModel::headerData(int section, Qt::Orientation orientation, int role) const |
||||
{ |
||||
if (role == Qt::TextAlignmentRole) |
||||
return QVariant(Qt::AlignLeft); |
||||
|
||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole) |
||||
return QVariant(); |
||||
|
||||
switch (section) { |
||||
case 0: |
||||
return i18n("Property"); |
||||
case 1: |
||||
return i18n("Value"); |
||||
default: |
||||
return QVariant(); |
||||
} |
||||
} |
||||
|
||||
bool CertificateModel::exportCertificateTo(const QString &path) |
||||
{ |
||||
const QUrl url = QUrl::fromUserInput(path); |
||||
if (!url.isLocalFile()) { |
||||
return false; |
||||
} |
||||
QFile targetFile(url.toLocalFile()); |
||||
if (!targetFile.open(QIODevice::WriteOnly)) { |
||||
return false; |
||||
} |
||||
const QByteArray data = m_certificateInfo.certificateData(); |
||||
return targetFile.write(data) == data.size(); |
||||
} |
||||
@ -0,0 +1,40 @@ |
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> |
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
#ifndef OKULAR_CERTIFICATEMODEL_H |
||||
#define OKULAR_CERTIFICATEMODEL_H |
||||
|
||||
#include <QAbstractTableModel> |
||||
#include <QVector> |
||||
|
||||
#include "core/signatureutils.h" |
||||
|
||||
class CertificateModel : public QAbstractTableModel |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
explicit CertificateModel(const Okular::CertificateInfo &certInfo, QObject *parent = nullptr); |
||||
|
||||
enum { PropertyKeyRole = Qt::UserRole, PropertyVisibleValueRole }; |
||||
|
||||
enum Property { Version, SerialNumber, Issuer, IssuedOn, ExpiresOn, Subject, PublicKey, KeyUsage, IssuerName, IssuerEmail, IssuerOrganization, SubjectName, SubjectEmail, SubjectOrganization, Sha1, Sha256 }; |
||||
Q_ENUM(Property) |
||||
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override; |
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; |
||||
|
||||
Q_INVOKABLE QString propertyVisibleValue(CertificateModel::Property p) const; |
||||
Q_INVOKABLE bool exportCertificateTo(const QString &path); |
||||
|
||||
private: |
||||
QVector<Property> m_certificateProperties; |
||||
const Okular::CertificateInfo &m_certificateInfo; |
||||
}; |
||||
|
||||
#endif |
||||
@ -0,0 +1,173 @@ |
||||
/* |
||||
* SPDX-FileCopyrightText: 2022 Albert Astals Cid <aacid@kde.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
import QtQuick 2.15 |
||||
import QtQuick.Window 2.15 |
||||
import QtQuick.Controls 2.15 as QQC2 |
||||
import QtQuick.Dialogs 1.3 as QQD |
||||
import QtQuick.Layouts 1.15 |
||||
import org.kde.kirigami 2.17 as Kirigami |
||||
import org.kde.okular 2.0 |
||||
|
||||
Kirigami.OverlaySheet |
||||
{ |
||||
id: root |
||||
|
||||
property var certificateModel |
||||
|
||||
title: i18n("Certificate Viewer") |
||||
|
||||
ColumnLayout { |
||||
// Without this the width is unreasonably narrow, potentially |
||||
// https://invent.kde.org/frameworks/kirigami/-/merge_requests/487 fixes it |
||||
// check when a kirigami with that is required as minimum version |
||||
Layout.preferredWidth: Math.min(Window.window.width, 360) |
||||
|
||||
QQC2.GroupBox { |
||||
Layout.fillWidth: true |
||||
|
||||
title: i18n("Issued By") |
||||
|
||||
Kirigami.FormLayout { |
||||
width: parent.width |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Common Name:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.IssuerName) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("EMail:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.IssuerEmail) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Organization:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.IssuerOrganization) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.GroupBox { |
||||
Layout.fillWidth: true |
||||
|
||||
title: i18n("Issued To") |
||||
|
||||
Kirigami.FormLayout { |
||||
width: parent.width |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Common Name:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.SubjectName) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("EMail:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.SubjectEmail) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Organization:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.SubjectOrganization) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.GroupBox { |
||||
Layout.fillWidth: true |
||||
|
||||
title: i18n("Validity") |
||||
|
||||
Kirigami.FormLayout { |
||||
width: parent.width |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Issued On:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.IssuedOn) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("Expires On:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.ExpiresOn) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.GroupBox { |
||||
Layout.fillWidth: true |
||||
|
||||
title: i18n("Fingerprints") |
||||
|
||||
Kirigami.FormLayout { |
||||
width: parent.width |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("SHA-1 Fingerprint:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.Sha1) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
Kirigami.FormData.label: i18n("SHA-256 Fingerprint:") |
||||
text: certificateModel.propertyVisibleValue(CertificateModel.Sha256) |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.DialogButtonBox { |
||||
Layout.topMargin: Kirigami.Units.largeSpacing |
||||
Layout.fillWidth: true |
||||
|
||||
QQC2.Button { |
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.ActionRole |
||||
text: i18n("Export...") |
||||
onClicked: fileDialog.open() |
||||
} |
||||
|
||||
QQC2.Button { |
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.DestructiveRole |
||||
text: i18n("Close") |
||||
icon.name: "dialog-close" |
||||
onClicked: root.close() |
||||
} |
||||
} |
||||
|
||||
QQD.FileDialog { |
||||
id: fileDialog |
||||
nameFilters: i18n("Certificate File (*.cer)") |
||||
folder: "file://" + userPaths.documents |
||||
selectExisting: false |
||||
onAccepted: { |
||||
if (!certificateModel.exportCertificateTo(fileDialog.fileUrl)) { |
||||
errorDialog.open(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO Use Kirigami.PromptDialog when we depend on KF >= 5.89 |
||||
// this way we can probably remove that ridiculous z value |
||||
QQC2.Dialog { |
||||
id: errorDialog |
||||
z: 200 |
||||
title: i18n("Error") |
||||
contentItem: QQC2.Label { |
||||
text: i18n("Could not export the certificate.") |
||||
} |
||||
standardButtons: QQC2.Dialog.Ok |
||||
|
||||
onAccepted: close(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,177 @@ |
||||
/* |
||||
* SPDX-FileCopyrightText: 2022 Albert Astals Cid <aacid@kde.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
import QtQuick 2.15 |
||||
import QtQuick.Window 2.15 |
||||
import QtQuick.Controls 2.15 as QQC2 |
||||
import QtQuick.Dialogs 1.3 as QQD |
||||
import QtQuick.Layouts 1.15 |
||||
import org.kde.kirigami 2.17 as Kirigami |
||||
|
||||
Kirigami.OverlaySheet |
||||
{ |
||||
id: root |
||||
|
||||
property alias signatureValidityText: signatureValidity.text |
||||
property alias documentModificationsText: documentModifications.text |
||||
property alias signerNameText: signerName.text |
||||
property alias signingTimeText: signingTime.text |
||||
property alias signingLocationText: signingLocation.text |
||||
property alias signingReasonText: signingReason.text |
||||
|
||||
property var certificateModel |
||||
property int signatureRevisionIndex: -1 |
||||
|
||||
signal saveSignatureSignedVersion(url path) |
||||
|
||||
title: i18n("Signature Properties") |
||||
|
||||
function showErrorDialog() { |
||||
errorDialog.open(); |
||||
} |
||||
|
||||
ColumnLayout { |
||||
// Without this the width is unreasonably narrow, potentially |
||||
// https://invent.kde.org/frameworks/kirigami/-/merge_requests/487 fixes it |
||||
// check when a kirigami with that is required as minimum version |
||||
Layout.preferredWidth: Math.min(Window.window.width, 360) |
||||
|
||||
QQC2.GroupBox { |
||||
Layout.fillWidth: true |
||||
title: i18n("Validity Status") |
||||
|
||||
Kirigami.FormLayout { |
||||
width: parent.width |
||||
QQC2.Label { |
||||
id: signatureValidity |
||||
Kirigami.FormData.label: i18n("Signature Validity:") |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
id: documentModifications |
||||
Kirigami.FormData.label: i18n("Document Modifications:") |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
QQC2.GroupBox { |
||||
title: i18n("Additional Information") |
||||
Layout.fillWidth: true |
||||
|
||||
Kirigami.FormLayout { |
||||
id: additionalInformationLayout |
||||
|
||||
width: parent.width |
||||
QQC2.Label { |
||||
id: signerName |
||||
Kirigami.FormData.label: i18n("Signed By:") |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
id: signingTime |
||||
Kirigami.FormData.label: i18n("Signing Time:") |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
id: signingReason |
||||
Kirigami.FormData.label: i18n("Reason:") |
||||
visible: text |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
QQC2.Label { |
||||
id: signingLocation |
||||
Kirigami.FormData.label: i18n("Location:") |
||||
visible: text |
||||
wrapMode: Text.Wrap |
||||
Layout.fillWidth: true |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.GroupBox { |
||||
title: i18n("Document Version") |
||||
Layout.fillWidth: true |
||||
|
||||
visible: root.signatureRevisionIndex >= 0 |
||||
|
||||
RowLayout { |
||||
width: parent.width |
||||
|
||||
QQC2.Label { |
||||
Layout.fillWidth: true |
||||
text: i18nc("Document Revision <current> of <total>", "Document Revision %1 of %2", root.signatureRevisionIndex + 1, documentItem.signaturesModel.count) |
||||
wrapMode: Text.Wrap |
||||
} |
||||
QQC2.Button { |
||||
text: i18n("Save Signed Version...") |
||||
onClicked: { |
||||
fileDialog.open(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.DialogButtonBox { |
||||
Layout.topMargin: Kirigami.Units.largeSpacing |
||||
Layout.fillWidth: true |
||||
|
||||
QQC2.Button { |
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.ActionRole |
||||
text: i18n("View Certificate...") |
||||
onClicked: { |
||||
var dialog = dialogComponent.createObject(Window.window, { |
||||
certificateModel: root.certificateModel |
||||
}) |
||||
dialog.open() |
||||
} |
||||
|
||||
Component { |
||||
id: dialogComponent |
||||
CertificateViewerDialog { |
||||
onSheetOpenChanged: if(!sheetOpen) { |
||||
destroy(1000) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
QQC2.Button { |
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.DestructiveRole |
||||
text: i18n("Close") |
||||
icon.name: "dialog-close" |
||||
onClicked: root.close() |
||||
} |
||||
} |
||||
|
||||
QQD.FileDialog { |
||||
id: fileDialog |
||||
folder: "file://" + userPaths.documents |
||||
selectExisting: false |
||||
onAccepted: { |
||||
root.saveSignatureSignedVersion(fileDialog.fileUrl); |
||||
} |
||||
} |
||||
|
||||
// TODO Use Kirigami.PromptDialog when we depend on KF >= 5.89 |
||||
// this way we can probably remove that ridiculous z value |
||||
QQC2.Dialog { |
||||
id: errorDialog |
||||
z: 200 |
||||
title: i18n("Error") |
||||
contentItem: QQC2.Label { |
||||
text: i18n("Could not save the signature.") |
||||
} |
||||
standardButtons: QQC2.Dialog.Ok |
||||
|
||||
onAccepted: close(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
/* |
||||
SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org> |
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
import QtQuick 2.15 |
||||
import QtQuick.Controls 2.15 as QQC2 |
||||
import QtQuick.Window 2.15 |
||||
import org.kde.kirigami 2.17 as Kirigami |
||||
import org.kde.kitemmodels 1.0 |
||||
|
||||
QQC2.ScrollView { |
||||
id: root |
||||
|
||||
signal dialogOpened |
||||
|
||||
ListView { |
||||
model: KDescendantsProxyModel { |
||||
model: documentItem.signaturesModel |
||||
expandsByDefault: false |
||||
} |
||||
|
||||
delegate: TreeItem { |
||||
|
||||
function displayString(str) { |
||||
return str ? str : i18n("Not Available"); |
||||
} |
||||
|
||||
text: model.display |
||||
onClicked: { |
||||
if (!model.isUnsignedSignature) { |
||||
var dialog = dialogComponent.createObject(Window.window, { |
||||
signatureValidityText: model.readableStatus, |
||||
documentModificationsText: model.readableModificationSummary, |
||||
signerNameText: displayString(model.signerName), |
||||
signingTimeText: displayString(model.signingTime), |
||||
signingLocationText: model.signingLocation, |
||||
signingReasonText: model.signingReason, |
||||
certificateModel: model.certificateModel, |
||||
signatureRevisionIndex: model.signatureRevisionIndex |
||||
}) |
||||
dialog.open() |
||||
root.dialogOpened(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Component { |
||||
id: dialogComponent |
||||
SignaturePropertiesDialog { |
||||
id: dialog |
||||
onSheetOpenChanged: if(!sheetOpen) { |
||||
destroy(1000) |
||||
} |
||||
onSaveSignatureSignedVersion: (path) => { |
||||
if (!documentItem.signaturesModel.saveSignedVersion(signatureRevisionIndex, path)) { |
||||
dialog.showErrorDialog(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,127 @@ |
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> |
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
#include "signaturepartutils.h" |
||||
|
||||
#include "core/document.h" |
||||
#include "core/form.h" |
||||
#include "core/page.h" |
||||
#include "pageview.h" |
||||
|
||||
#include <QFileDialog> |
||||
#include <QFileInfo> |
||||
#include <QInputDialog> |
||||
#include <QMimeDatabase> |
||||
|
||||
#include <KLocalizedString> |
||||
#include <KMessageBox> |
||||
|
||||
namespace SignaturePartUtils |
||||
{ |
||||
std::unique_ptr<Okular::CertificateInfo> getCertificateAndPasswordForSigning(PageView *pageView, Okular::Document *doc, QString *password, QString *documentPassword) |
||||
{ |
||||
const Okular::CertificateStore *certStore = doc->certificateStore(); |
||||
bool userCancelled, nonDateValidCerts; |
||||
QList<Okular::CertificateInfo *> certs = certStore->signingCertificatesForNow(&userCancelled, &nonDateValidCerts); |
||||
if (userCancelled) { |
||||
return nullptr; |
||||
} |
||||
|
||||
if (certs.isEmpty()) { |
||||
pageView->showNoSigningCertificatesDialog(nonDateValidCerts); |
||||
return nullptr; |
||||
} |
||||
|
||||
QStringList items; |
||||
QHash<QString, Okular::CertificateInfo *> nickToCert; |
||||
for (auto cert : qAsConst(certs)) { |
||||
items.append(cert->nickName()); |
||||
nickToCert[cert->nickName()] = cert; |
||||
} |
||||
|
||||
bool resok = false; |
||||
const QString certNicknameToUse = QInputDialog::getItem(pageView, i18n("Select certificate to sign with"), i18n("Certificates:"), items, 0, false, &resok); |
||||
|
||||
if (!resok) { |
||||
qDeleteAll(certs); |
||||
return nullptr; |
||||
} |
||||
|
||||
// I could not find any case in which i need to enter a password to use the certificate, seems that once you unlcok the firefox/NSS database
|
||||
// you don't need a password anymore, but still there's code to do that in NSS so we have code to ask for it if needed. What we do is
|
||||
// ask if the empty password is fine, if it is we don't ask the user anything, if it's not, we ask for a password
|
||||
Okular::CertificateInfo *cert = nickToCert.value(certNicknameToUse); |
||||
bool passok = cert->checkPassword(*password); |
||||
while (!passok) { |
||||
const QString title = i18n("Enter password (if any) to unlock certificate: %1", certNicknameToUse); |
||||
bool ok; |
||||
*password = QInputDialog::getText(pageView, i18n("Enter certificate password"), title, QLineEdit::Password, QString(), &ok); |
||||
if (ok) { |
||||
passok = cert->checkPassword(*password); |
||||
} else { |
||||
passok = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (doc->metaData(QStringLiteral("DocumentHasPassword")).toString() == QLatin1String("yes")) { |
||||
*documentPassword = QInputDialog::getText(pageView, i18n("Enter document password"), i18n("Enter document password"), QLineEdit::Password, QString(), &passok); |
||||
} |
||||
|
||||
if (passok) { |
||||
certs.removeOne(cert); |
||||
} |
||||
qDeleteAll(certs); |
||||
|
||||
return passok ? std::unique_ptr<Okular::CertificateInfo>(cert) : std::unique_ptr<Okular::CertificateInfo>(); |
||||
} |
||||
|
||||
QString getFileNameForNewSignedFile(PageView *pageView, Okular::Document *doc) |
||||
{ |
||||
QMimeDatabase db; |
||||
const QString typeName = doc->documentInfo().get(Okular::DocumentInfo::MimeType); |
||||
const QMimeType mimeType = db.mimeTypeForName(typeName); |
||||
const QString mimeTypeFilter = i18nc("File type name and pattern", "%1 (%2)", mimeType.comment(), mimeType.globPatterns().join(QLatin1Char(' '))); |
||||
|
||||
const QUrl currentFileUrl = doc->currentDocument(); |
||||
const QFileInfo currentFileInfo(currentFileUrl.fileName()); |
||||
const QString localFilePathIfAny = currentFileUrl.isLocalFile() ? QFileInfo(currentFileUrl.path()).canonicalPath() + QLatin1Char('/') : QString(); |
||||
const QString newFileName = |
||||
localFilePathIfAny + i18nc("Used when suggesting a new name for a digitally signed file. %1 is the old file name and %2 it's extension", "%1_signed.%2", currentFileInfo.baseName(), currentFileInfo.completeSuffix()); |
||||
|
||||
return QFileDialog::getSaveFileName(pageView, i18n("Save Signed File As"), newFileName, mimeTypeFilter); |
||||
} |
||||
|
||||
void signUnsignedSignature(const Okular::FormFieldSignature *form, PageView *pageView, Okular::Document *doc) |
||||
{ |
||||
Q_ASSERT(form && form->signatureType() == Okular::FormFieldSignature::UnsignedSignature); |
||||
QString password, documentPassword; |
||||
const std::unique_ptr<Okular::CertificateInfo> cert = getCertificateAndPasswordForSigning(pageView, doc, &password, &documentPassword); |
||||
if (!cert) { |
||||
return; |
||||
} |
||||
|
||||
Okular::NewSignatureData data; |
||||
data.setCertNickname(cert->nickName()); |
||||
data.setCertSubjectCommonName(cert->subjectInfo(Okular::CertificateInfo::CommonName)); |
||||
data.setPassword(password); |
||||
data.setDocumentPassword(documentPassword); |
||||
password.clear(); |
||||
documentPassword.clear(); |
||||
|
||||
const QString newFilePath = getFileNameForNewSignedFile(pageView, doc); |
||||
|
||||
if (!newFilePath.isEmpty()) { |
||||
const bool success = form->sign(data, newFilePath); |
||||
if (success) { |
||||
emit pageView->requestOpenFile(newFilePath, form->page()->number() + 1); |
||||
} else { |
||||
KMessageBox::error(pageView, i18nc("%1 is a file path", "Could not sign. Invalid certificate password or could not write to '%1'", newFilePath)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,24 @@ |
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> |
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later |
||||
*/ |
||||
|
||||
#ifndef OKULAR_SIGNATUREPARTUTILS_H |
||||
#define OKULAR_SIGNATUREPARTUTILS_H |
||||
|
||||
#include <memory> |
||||
|
||||
#include "gui/signatureguiutils.h" |
||||
|
||||
class PageView; |
||||
|
||||
namespace SignaturePartUtils |
||||
{ |
||||
std::unique_ptr<Okular::CertificateInfo> getCertificateAndPasswordForSigning(PageView *pageView, Okular::Document *doc, QString *password, QString *documentPassword); |
||||
QString getFileNameForNewSignedFile(PageView *pageView, Okular::Document *doc); |
||||
void signUnsignedSignature(const Okular::FormFieldSignature *form, PageView *pageView, Okular::Document *doc); |
||||
|
||||
} |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue