mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-02 22:23:43 +00:00
cb691f70dc
Summary: fix clazy warning Reviewers: aacid Reviewed By: aacid Subscribers: nicolasfella, okular-devel Tags: #okular Differential Revision: https://phabricator.kde.org/D20832
344 lines
13 KiB
C++
344 lines
13 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2018 by Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
|
|
#include "certificateviewer.h"
|
|
|
|
#include <KMessageBox>
|
|
#include <KLocalizedString>
|
|
#include <KColumnResizer>
|
|
|
|
#include <QDebug>
|
|
#include <QLabel>
|
|
#include <QTextEdit>
|
|
#include <QTreeView>
|
|
#include <QGroupBox>
|
|
#include <QFormLayout>
|
|
#include <QPushButton>
|
|
#include <QVBoxLayout>
|
|
#include <QFileDialog>
|
|
#include <QDialogButtonBox>
|
|
#include <QCryptographicHash>
|
|
|
|
#include "signatureguiutils.h"
|
|
|
|
// DN (DistinguishedName) attributes can be
|
|
// C Country
|
|
// CN Common name
|
|
// DC Domain component
|
|
// E E-mail address
|
|
// EMAIL E-mail address (preferred)
|
|
// EMAILADDRESS E-mail address
|
|
// L Locality
|
|
// O Organization name
|
|
// OU Organizational unit name
|
|
// PC Postal code
|
|
// S State or province
|
|
// SN Family name
|
|
// SP State or province
|
|
// ST State or province (preferred)
|
|
// STREET Street
|
|
// T Title
|
|
|
|
|
|
// CN=James Hacker,
|
|
// L=Basingstoke,
|
|
// O=Widget Inc,
|
|
// C=GB
|
|
// CN=L. Eagle, O="Sue, Grabbit and Runn", C=GB
|
|
// CN=L. Eagle, O=Sue\, Grabbit and Runn, C=GB
|
|
|
|
|
|
// This is a poor man's attempt at parsing DN, if it fails it is not a problem since it's only for display in a list
|
|
static QString splitDNAttributes(const QStringList &text)
|
|
{
|
|
const QStringList attributes = { "C", "CN", "DC", "E", "EMAIL", "EMAILADDRESS", "L", "O", "OU", "PC", "S", "SN", "SP", "ST", "STREET", "T" };
|
|
|
|
for ( const QString &t : text )
|
|
{
|
|
for ( const QString &attribute : attributes )
|
|
{
|
|
const QRegularExpression re( QStringLiteral( "(.*),\\s*(%1=.*)" ).arg( attribute ), QRegularExpression::DotMatchesEverythingOption );
|
|
const QRegularExpressionMatch match = re.match( t );
|
|
if ( match.hasMatch() )
|
|
{
|
|
QStringList results = text;
|
|
const int index = results.indexOf( t );
|
|
results.removeAt( index );
|
|
results.insert( index, match.captured( 2 ) );
|
|
results.insert( index, match.captured( 1 ) );
|
|
return splitDNAttributes( results );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean escaped commas
|
|
QStringList result = text;
|
|
for ( QString &t : result )
|
|
{
|
|
t.replace( QLatin1String("\\,"), QLatin1String(",") );
|
|
}
|
|
|
|
// Clean up quoted attributes
|
|
for ( QString &t : result )
|
|
{
|
|
for ( const QString &attribute : attributes )
|
|
{
|
|
const QRegularExpression re( QStringLiteral( "%1=\"(.*)\"" ).arg( attribute ) );
|
|
const QRegularExpressionMatch match = re.match( t );
|
|
if ( match.hasMatch() )
|
|
{
|
|
t = attribute + "=" + match.captured( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return result.join(QStringLiteral("\n"));
|
|
}
|
|
|
|
static QString splitDNAttributes(const QString &text)
|
|
{
|
|
return splitDNAttributes( QStringList{ text } );
|
|
}
|
|
|
|
|
|
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");
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
static QString propertyVisibleValue( CertificateModel::Property p, const Okular::CertificateInfo &certInfo )
|
|
{
|
|
switch ( p )
|
|
{
|
|
case CertificateModel::Version:
|
|
return i18n("V%1", QString::number( certInfo.version() ));
|
|
case CertificateModel::SerialNumber:
|
|
return certInfo.serialNumber().toHex(' ');
|
|
case CertificateModel::Issuer:
|
|
return certInfo.issuerInfo(Okular::CertificateInfo::DistinguishedName);
|
|
case CertificateModel::IssuedOn:
|
|
return certInfo.validityStart().toString( Qt::DefaultLocaleLongDate );
|
|
case CertificateModel::ExpiresOn:
|
|
return certInfo.validityEnd().toString( Qt::DefaultLocaleLongDate );
|
|
case CertificateModel::Subject:
|
|
return certInfo.subjectInfo(Okular::CertificateInfo::DistinguishedName);
|
|
case CertificateModel::PublicKey:
|
|
return i18n("%1 (%2 bits)", SignatureGuiUtils::getReadablePublicKeyType( certInfo.publicKeyType() ),
|
|
certInfo.publicKeyStrength());
|
|
case CertificateModel::KeyUsage:
|
|
return SignatureGuiUtils::getReadableKeyUsageCommaSeparated( certInfo.keyUsageExtensions() );
|
|
}
|
|
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], m_certificateInfo );
|
|
default:
|
|
return QString();
|
|
}
|
|
case PropertyKeyRole:
|
|
return m_certificateProperties[row];
|
|
case PropertyVisibleValueRole:
|
|
return propertyVisibleValue( m_certificateProperties[row], m_certificateInfo );
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
CertificateViewer::CertificateViewer( const Okular::CertificateInfo &certInfo, QWidget *parent )
|
|
: KPageDialog( parent ), m_certificateInfo( certInfo )
|
|
{
|
|
setModal( true );
|
|
setMinimumSize( QSize( 500, 500 ));
|
|
setFaceType( Tabbed );
|
|
setWindowTitle( i18n("Certificate Viewer") );
|
|
setStandardButtons( QDialogButtonBox::Close );
|
|
|
|
auto exportBtn = new QPushButton( i18n("Export...") );
|
|
connect( exportBtn, &QPushButton::clicked, this, &CertificateViewer::exportCertificate );
|
|
addActionButton( exportBtn );
|
|
|
|
// General tab
|
|
auto generalPage = new QFrame( this );
|
|
addPage( generalPage, i18n("General") );
|
|
|
|
auto issuerBox = new QGroupBox( i18n("Issued By"), generalPage );
|
|
auto issuerFormLayout = new QFormLayout( issuerBox );
|
|
issuerFormLayout->setLabelAlignment( Qt::AlignLeft );
|
|
issuerFormLayout->addRow( i18n("Common Name(CN)"), new QLabel( m_certificateInfo.issuerInfo( Okular::CertificateInfo::CommonName ) ) );
|
|
issuerFormLayout->addRow( i18n("EMail"), new QLabel( m_certificateInfo.issuerInfo( Okular::CertificateInfo::EmailAddress ) ) );
|
|
issuerFormLayout->addRow( i18n("Organization(O)"), new QLabel( m_certificateInfo.issuerInfo( Okular::CertificateInfo::Organization ) ) );
|
|
|
|
auto subjectBox = new QGroupBox( i18n("Issued To"), generalPage );
|
|
auto subjectFormLayout = new QFormLayout( subjectBox );
|
|
subjectFormLayout->setLabelAlignment( Qt::AlignLeft );
|
|
subjectFormLayout->addRow( i18n("Common Name(CN)"), new QLabel( m_certificateInfo.subjectInfo( Okular::CertificateInfo::CommonName ) ) );
|
|
subjectFormLayout->addRow( i18n("EMail"), new QLabel( m_certificateInfo.subjectInfo( Okular::CertificateInfo::EmailAddress ) ) );
|
|
subjectFormLayout->addRow( i18n("Organization(O)"), new QLabel( m_certificateInfo.subjectInfo( Okular::CertificateInfo::Organization ) ) );
|
|
|
|
auto validityBox = new QGroupBox( i18n("Validity"), generalPage );
|
|
auto validityFormLayout = new QFormLayout( validityBox );
|
|
validityFormLayout->setLabelAlignment( Qt::AlignLeft );
|
|
validityFormLayout->addRow( i18n("Issued On"), new QLabel( m_certificateInfo.validityStart().toString( Qt::DefaultLocaleLongDate ) ) );
|
|
validityFormLayout->addRow( i18n("Expires On"), new QLabel( m_certificateInfo.validityEnd().toString( Qt::DefaultLocaleLongDate ) ) );
|
|
|
|
auto fingerprintBox = new QGroupBox( i18n("Fingerprints"), generalPage );
|
|
auto fingerprintFormLayout = new QFormLayout( fingerprintBox );
|
|
fingerprintFormLayout->setLabelAlignment( Qt::AlignLeft );
|
|
QByteArray certData = m_certificateInfo.certificateData();
|
|
auto sha1Label = new QLabel( QString( QCryptographicHash::hash( certData, QCryptographicHash::Sha1 ).toHex(' ') ) );
|
|
sha1Label->setWordWrap( true );
|
|
auto sha256Label = new QLabel( QString( QCryptographicHash::hash( certData, QCryptographicHash::Sha256 ).toHex(' ') ) );
|
|
sha256Label->setWordWrap( true );
|
|
fingerprintFormLayout->addRow( i18n("SHA-1 Fingerprint"), sha1Label );
|
|
fingerprintFormLayout->addRow( i18n("SHA-256 Fingerprint"), sha256Label );
|
|
|
|
auto generalPageLayout = new QVBoxLayout( generalPage );
|
|
generalPageLayout->addWidget( issuerBox );
|
|
generalPageLayout->addWidget( subjectBox );
|
|
generalPageLayout->addWidget( validityBox );
|
|
generalPageLayout->addWidget( fingerprintBox );
|
|
generalPageLayout->addStretch();
|
|
|
|
//force column 1 to have same width
|
|
auto resizer = new KColumnResizer( this );
|
|
resizer->addWidgetsFromLayout( issuerBox->layout(), 0 );
|
|
resizer->addWidgetsFromLayout( subjectBox->layout(), 0 );
|
|
resizer->addWidgetsFromLayout( validityBox->layout(), 0 );
|
|
resizer->addWidgetsFromLayout( fingerprintBox->layout(), 0 );
|
|
|
|
// Details tab
|
|
auto detailsFrame = new QFrame( this );
|
|
addPage( detailsFrame, i18n("Details") );
|
|
auto certDataLabel = new QLabel( i18n("Certificate Data:") );
|
|
auto certTree = new QTreeView( this );
|
|
certTree->setIndentation( 0 );
|
|
m_certificateModel = new CertificateModel( m_certificateInfo, this );
|
|
certTree->setModel( m_certificateModel );
|
|
connect( certTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &CertificateViewer::updateText );
|
|
m_propertyText = new QTextEdit( this );
|
|
m_propertyText->setReadOnly( true );
|
|
|
|
auto detailsPageLayout = new QVBoxLayout( detailsFrame );
|
|
detailsPageLayout->addWidget( certDataLabel );
|
|
detailsPageLayout->addWidget( certTree );
|
|
detailsPageLayout->addWidget( m_propertyText );
|
|
}
|
|
|
|
void CertificateViewer::updateText( const QModelIndex &index )
|
|
{
|
|
QString text;
|
|
const CertificateModel::Property key = m_certificateModel->data( index, CertificateModel::PropertyKeyRole ).value<CertificateModel::Property>();
|
|
switch ( key )
|
|
{
|
|
case CertificateModel::SerialNumber:
|
|
case CertificateModel::Version:
|
|
case CertificateModel::IssuedOn:
|
|
case CertificateModel::ExpiresOn:
|
|
text = m_certificateModel->data( index, CertificateModel::PropertyVisibleValueRole ).toString();
|
|
break;
|
|
case CertificateModel::Issuer:
|
|
case CertificateModel::Subject:
|
|
text = splitDNAttributes( m_certificateModel->data( index, CertificateModel::PropertyVisibleValueRole ).toString() );
|
|
break;
|
|
case CertificateModel::PublicKey:
|
|
text = m_certificateInfo.publicKey().toHex(' ');
|
|
break;
|
|
case CertificateModel::KeyUsage:
|
|
text = SignatureGuiUtils::getReadableKeyUsageNewLineSeparated( m_certificateInfo.keyUsageExtensions() );
|
|
break;
|
|
}
|
|
m_propertyText->setText( text );
|
|
}
|
|
|
|
void CertificateViewer::exportCertificate()
|
|
{
|
|
const QString caption = i18n("Where do you want to save this certificate?");
|
|
const QString path = QFileDialog::getSaveFileName( this, caption, QStringLiteral("Certificate.cer"), i18n("Certificate File (*.cer)") );
|
|
if ( !path.isEmpty() )
|
|
{
|
|
QFile targetFile( path );
|
|
targetFile.open( QIODevice::WriteOnly );
|
|
if ( targetFile.write( m_certificateInfo.certificateData() ) == -1 )
|
|
{
|
|
KMessageBox::error( this, i18n("Unable to export certificate!") );
|
|
}
|
|
targetFile.close();
|
|
}
|
|
}
|