Security information

This commit is contained in:
Jakub Melka 2019-12-21 15:02:11 +01:00
parent c228cf6d24
commit f2f398e82b
7 changed files with 183 additions and 5 deletions

View File

@ -64,6 +64,18 @@ const PDFDictionary* PDFDocument::getTrailerDictionary() const
return nullptr;
}
QByteArray PDFDocument::getVersion() const
{
QByteArray result = m_catalog.getVersion();
if (result.isEmpty() && m_info.version.isValid())
{
result = QString("%1.%2").arg(m_info.version.major).arg(m_info.version.minor).toLatin1();
}
return result;
}
void PDFDocument::init()
{
initInfo();
@ -164,6 +176,32 @@ void PDFDocument::initInfo()
throw PDFException(tr("Bad format of document info entry in trailer dictionary. Trapping information expected"));
}
}
// Scan for extra items
constexpr const char* PREDEFINED_ITEMS[] = { PDF_DOCUMENT_INFO_ENTRY_TITLE, PDF_DOCUMENT_INFO_ENTRY_AUTHOR, PDF_DOCUMENT_INFO_ENTRY_SUBJECT,
PDF_DOCUMENT_INFO_ENTRY_KEYWORDS, PDF_DOCUMENT_INFO_ENTRY_CREATOR, PDF_DOCUMENT_INFO_ENTRY_PRODUCER,
PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE, PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE, PDF_DOCUMENT_INFO_ENTRY_TRAPPED };
for (size_t i = 0; i < infoDictionary->getCount(); ++i)
{
const QByteArray& key = infoDictionary->getKey(i);
if (std::none_of(std::begin(PREDEFINED_ITEMS), std::end(PREDEFINED_ITEMS), [&key](const char* item) { return item == key; }))
{
const PDFObject& value = getObject(infoDictionary->getValue(i));
if (value.isString())
{
const QByteArray& stringValue = value.getString();
QDateTime dateTime = PDFEncoding::convertToDateTime(stringValue);
if (dateTime.isValid())
{
m_info.extra[key] = dateTime;
}
else
{
m_info.extra[key] = PDFEncoding::convertTextString(stringValue);
}
}
}
}
}
else if (!info.isNull()) // Info may be invalid...
{

View File

@ -305,7 +305,9 @@ public:
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
/// Info about the document. Title, Author, Keywords...
/// Info about the document. Title, Author, Keywords... It also stores "extra"
/// values, which are in info dictionary. They can be either strings, or date
/// time (QString or QDateTime).
struct Info
{
/// Indicates, that document was modified that it includes trapping information.
@ -326,6 +328,8 @@ public:
QDateTime creationDate;
QDateTime modifiedDate;
Trapped trapped = Trapped::Unknown;
PDFVersion version;
std::map<QByteArray, QVariant> extra;
};
/// Returns info about the document (title, author, etc.)
@ -356,13 +360,20 @@ public:
/// Returns the trailer dictionary
const PDFDictionary* getTrailerDictionary() const;
/// Returns version of the PDF document. Version can be taken from catalog,
/// or from PDF file header. Version from catalog has precedence over version from
/// header.
QByteArray getVersion() const;
private:
friend class PDFDocumentReader;
explicit PDFDocument(PDFObjectStorage&& storage) :
explicit PDFDocument(PDFObjectStorage&& storage, PDFVersion version) :
m_pdfObjectStorage(std::move(storage))
{
init();
m_info.version = version;
}
/// Initialize data based on object in the storage.

View File

@ -481,7 +481,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
std::for_each(std::execution::parallel_policy(), objectStreams.cbegin(), objectStreams.cend(), processObjectStream);
PDFObjectStorage storage(std::move(objects), PDFObject(xrefTable.getTrailerDictionary()), std::move(securityHandler));
return PDFDocument(std::move(storage));
return PDFDocument(std::move(storage), m_version);
}
catch (PDFException parserException)
{

View File

@ -92,6 +92,7 @@ public:
enum class AuthorizationResult
{
NoAuthorizationRequired,
UserAuthorized,
OwnerAuthorized,
Failed,
@ -146,6 +147,12 @@ public:
/// Returns true, if metadata are encrypted
virtual bool isMetadataEncrypted() const = 0;
/// Returns result of authorization process
virtual AuthorizationResult getAuthorizationResult() const = 0;
/// Returns version of the encryption
int getVersion() const { return m_V; }
/// Creates a security handler from the object. If object is null, then
/// "None" security handler is created. If error occurs, then exception is thrown.
/// \param encryptionDictionaryObject Encryption dictionary object
@ -185,7 +192,8 @@ public:
virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference, EncryptionScope) const override { return data; }
virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray&, PDFObjectReference) const override { return data; }
virtual bool isMetadataEncrypted() const override { return true; }
virtual bool isAllowed(Permission) const { return true; }
virtual bool isAllowed(Permission) const override { return true; }
virtual AuthorizationResult getAuthorizationResult() const override { return AuthorizationResult::NoAuthorizationRequired; }
};
/// Specifies the security using standard security handler (see PDF specification
@ -199,6 +207,7 @@ public:
virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const override;
virtual bool isMetadataEncrypted() const override { return m_encryptMetadata; }
virtual bool isAllowed(Permission permission) const { return m_authorizationData.authorizationResult == AuthorizationResult::OwnerAuthorized || (m_permissions & static_cast<uint32_t>(permission)); }
virtual AuthorizationResult getAuthorizationResult() const override { return m_authorizationData.authorizationResult; }
struct AuthorizationData
{

View File

@ -37,6 +37,7 @@ PDFDocumentPropertiesDialog::PDFDocumentPropertiesDialog(const pdf::PDFDocument*
initializeProperties(document);
initializeFileInfoProperties(fileInfo);
initializeSecurity(document);
const int defaultWidth = PDFWidgetUtils::getPixelSize(this, 240.0);
const int defaultHeight = PDFWidgetUtils::getPixelSize(this, 200.0);
@ -57,6 +58,7 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
const pdf::PDFDocument::Info* info = document->getInfo();
const pdf::PDFCatalog* catalog = document->getCatalog();
new QTreeWidgetItem(propertiesRoot, { tr("PDF version"), QString::fromLatin1(document->getVersion()) });
new QTreeWidgetItem(propertiesRoot, { tr("Title"), info->title });
new QTreeWidgetItem(propertiesRoot, { tr("Subject"), info->subject });
new QTreeWidgetItem(propertiesRoot, { tr("Author"), info->author });
@ -65,7 +67,6 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
new QTreeWidgetItem(propertiesRoot, { tr("Producer"), info->producer });
new QTreeWidgetItem(propertiesRoot, { tr("Creation date"), locale.toString(info->creationDate) });
new QTreeWidgetItem(propertiesRoot, { tr("Modified date"), locale.toString(info->modifiedDate) });
new QTreeWidgetItem(propertiesRoot, { tr("Version"), QString::fromLatin1(catalog->getVersion()) });
QString trapped;
switch (info->trapped)
@ -105,6 +106,20 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
ui->propertiesTreeWidget->addTopLevelItem(propertiesRoot);
ui->propertiesTreeWidget->addTopLevelItem(contentRoot);
if (!info->extra.empty())
{
QTreeWidgetItem* customRoot = new QTreeWidgetItem({ tr("Custom properties") });
for (const auto& item : info->extra)
{
QString key = QString::fromLatin1(item.first);
QVariant valueVariant = item.second;
QString value = (valueVariant.type() == QVariant::DateTime) ? locale.toString(valueVariant.toDateTime()) : valueVariant.toString();
new QTreeWidgetItem(customRoot, { key, value });
}
ui->propertiesTreeWidget->addTopLevelItem(customRoot);
}
ui->propertiesTreeWidget->expandAll();
ui->propertiesTreeWidget->resizeColumnToContents(0);
}
@ -140,4 +155,77 @@ void PDFDocumentPropertiesDialog::initializeFileInfoProperties(const PDFFileInfo
ui->fileInfoTreeWidget->resizeColumnToContents(0);
}
void PDFDocumentPropertiesDialog::initializeSecurity(const pdf::PDFDocument* document)
{
QLocale locale;
QTreeWidgetItem* securityRoot = new QTreeWidgetItem({ tr("Security") });
const pdf::PDFSecurityHandler* securityHandler = document->getStorage().getSecurityHandler();
const pdf::EncryptionMode mode = securityHandler->getMode();
QString modeString;
switch (mode)
{
case pdf::EncryptionMode::None:
modeString = tr("None");
break;
case pdf::EncryptionMode::Standard:
modeString = tr("Standard");
break;
case pdf::EncryptionMode::Custom:
modeString = tr("Custom");
break;
default:
Q_ASSERT(false);
break;
}
QString authorizationMode;
switch (securityHandler->getAuthorizationResult())
{
case pdf::PDFSecurityHandler::AuthorizationResult::NoAuthorizationRequired:
authorizationMode = tr("No authorization required");
break;
case pdf::PDFSecurityHandler::AuthorizationResult::OwnerAuthorized:
authorizationMode = tr("Authorized as owner");
break;
case pdf::PDFSecurityHandler::AuthorizationResult::UserAuthorized:
authorizationMode = tr("Authorized as user");
break;
default:
Q_ASSERT(false);
break;
}
new QTreeWidgetItem(securityRoot, { tr("Document encryption"), modeString });
new QTreeWidgetItem(securityRoot, { tr("Authorized as"), authorizationMode });
new QTreeWidgetItem(securityRoot, { tr("Metadata encrypted"), securityHandler->isMetadataEncrypted() ? tr("Yes") : tr("No") });
new QTreeWidgetItem(securityRoot, { tr("Version"), locale.toString(securityHandler->getVersion()) });
QTreeWidgetItem* permissionsRoot = new QTreeWidgetItem({ tr("Permissions") });
auto addPermissionInfo = [securityHandler, permissionsRoot](QString caption, pdf::PDFSecurityHandler::Permission permission)
{
new QTreeWidgetItem(permissionsRoot, { caption, securityHandler->isAllowed(permission) ? tr("Yes") : tr("No")});
};
addPermissionInfo(tr("Print (low resolution)"), pdf::PDFSecurityHandler::Permission::PrintLowResolution);
addPermissionInfo(tr("Print (high resolution)"), pdf::PDFSecurityHandler::Permission::PrintHighResolution);
addPermissionInfo(tr("Content extraction"), pdf::PDFSecurityHandler::Permission::CopyContent);
addPermissionInfo(tr("Content extraction (accessibility)"), pdf::PDFSecurityHandler::Permission::Accessibility);
addPermissionInfo(tr("Page assembling"), pdf::PDFSecurityHandler::Permission::Assemble);
addPermissionInfo(tr("Modify content"), pdf::PDFSecurityHandler::Permission::Modify);
addPermissionInfo(tr("Modify interactive items"), pdf::PDFSecurityHandler::Permission::ModifyInteractiveItems);
addPermissionInfo(tr("Fill form fields"), pdf::PDFSecurityHandler::Permission::ModifyFormFields);
ui->securityTreeWidget->addTopLevelItem(securityRoot);
ui->securityTreeWidget->addTopLevelItem(permissionsRoot);
ui->securityTreeWidget->expandAll();
ui->securityTreeWidget->resizeColumnToContents(0);
}
} // namespace pdfviewer

View File

@ -61,6 +61,7 @@ private:
void initializeProperties(const pdf::PDFDocument* document);
void initializeFileInfoProperties(const PDFFileInfo* fileInfo);
void initializeSecurity(const pdf::PDFDocument* document);
};
} // namespace pdfviewer

View File

@ -88,6 +88,37 @@
<attribute name="title">
<string>Security</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="securityGroupBox">
<property name="title">
<string>Security</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QTreeWidget" name="securityTreeWidget">
<property name="columnCount">
<number>2</number>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>