Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
typo3
qfq
Commits
e643c898
Commit
e643c898
authored
Nov 06, 2019
by
Carsten Rose
Browse files
Fixes bug #9512 : pdf merge fails on encrypted PDFs. Try to use qpdf to decrypt.
parent
23f0333f
Pipeline
#2631
passed with stages
in 2 minutes and 47 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Documentation/Manual.rst
View file @
e643c898
...
...
@@ -51,6 +51,7 @@ The following features are only tested / supported on linux hosts:
* General: QFQ is coded to run on Linux hosts, preferable on Debian derivates like Ubuntu.
* HTML to PDF conversion - command `wkhtmltopdf`.
* Concatenation of PDF files - command `pdfunite`.
* PDF decrypt (used for merge with pdfunite) - command `qpdf`.
* Mime type detection for uploads - command `file`.
...
...
@@ -66,12 +67,12 @@ To normalize UTF8 input, *php-intl* package is needed by
* normalizer::normalize()
For the `download`_ function, the programs `pdfunite` and `file` are necessary to concatenate PDF files.
For the `download`_ function, the programs `pdfunite`
, `qpdf`
and `file` are necessary to concatenate PDF files.
Preparation for Ubuntu::
sudo apt install php-intl
sudo apt install poppler-utils libxrender1 file pdf2svg # for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
sudo apt install poppler-utils libxrender1 file pdf2svg
pdfunite qpdf
# for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
sudo apt install inkscape imagemagick # to render thumbnails
.. _wkhtml:
...
...
extension/Classes/Core/Report/Download.php
View file @
e643c898
...
...
@@ -10,20 +10,19 @@
namespace
IMATHUZH\Qfq\Core\Report
;
use
IMATHUZH\Qfq\Core\Database\Database
;
use
IMATHUZH\Qfq\Core\Helper\DownloadPage
;
use
IMATHUZH\Qfq\Core\Helper\HelperFile
;
use
IMATHUZH\Qfq\Core\Helper\KeyValueStringParser
;
use
IMATHUZH\Qfq\Core\Store\Session
;
use
IMATHUZH\Qfq\Core\Store\Store
;
use
IMATHUZH\Qfq\Core\Store\Sip
;
use
IMATHUZH\Qfq\Core\Helper\Logger
;
use
IMATHUZH\Qfq\Core\Helper\OnArray
;
use
IMATHUZH\Qfq\Core\Helper\OnString
;
use
IMATHUZH\Qfq\Core\Helper\Logger
;
use
IMATHUZH\Qfq\Core\Helper\Sanitize
;
use
IMATHUZH\Qfq\Core\Helper\HelperFile
;
use
IMATHUZH\Qfq\Core\Helper\DownloadPage
;
use
IMATHUZH\Qfq\Core\QuickFormQuery
;
use
IMATHUZH\Qfq\Core\Database\Database
;
use
IMATHUZH\Qfq\Core\Helper\Support
;
use
IMATHUZH\Qfq\Core\QuickFormQuery
;
use
IMATHUZH\Qfq\Core\Store\Session
;
use
IMATHUZH\Qfq\Core\Store\Sip
;
use
IMATHUZH\Qfq\Core\Store\Store
;
/**
* Class Download
...
...
@@ -147,7 +146,7 @@ class Download {
Logger
::
logMessage
(
"Download:
$cmd
"
,
$this
->
downloadDebugLog
);
}
exec
(
$cmd
,
$output
,
$rc
);
$rc
=
$this
->
concatPdfFilesPdfUnite
(
$cmd
,
$output
);
if
(
$rc
!=
0
)
{
throw
new
\
DownloadException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
"Failed to merge PDF file"
,
...
...
@@ -158,6 +157,69 @@ class Download {
return
$concatFile
;
}
/**
* Fires the merge command.
* If for any reason the command fails: check if the reason is 'unencrypted files'.
* If 'yes': try to decrypt them with qpdf.
* After one decrypt, try merge again.
* Try to merge and decrypt as long as there are encrypted files.
*
* @param $cmd
* @param $rcOutput
* @return mixed
* @throws \DownloadException
* @throws \UserFormException
*/
private
function
concatPdfFilesPdfUnite
(
$cmd
,
&
$rcOutput
)
{
$last
=
''
;
$rcOutput
=
'-'
;
// Try to merge the PDFs as long as a problematic PDF has been repaired.
while
(
$last
!=
$rcOutput
)
{
$last
=
$rcOutput
;
// Remember last
// Merge
exec
(
$cmd
,
$rcOutput
,
$rc
);
if
(
$rc
==
0
)
{
break
;
// skip rest if everything is fine
}
// Possible output: "Unimplemented Feature: Could not merge encrypted files ('ct.18.06.092-097.pdf')"
$line
=
implode
(
','
,
$rcOutput
);
if
(
false
!==
strstr
(
$line
,
"Unimplemented Feature: Could not merge encrypted files ("
))
{
$arr
=
explode
(
"'"
,
$line
,
3
);
if
(
!
empty
(
$arr
[
1
])
&&
file_exists
(
$arr
[
1
]))
{
$file
=
$arr
[
1
];
// problematic file
// Create a backup file: only one per day!
$backup
=
$file
.
date
(
'.Y-m-d'
);
if
(
!
file_exists
(
$backup
))
{
HelperFile
::
copy
(
$file
,
$backup
);
}
$cmdQpdf
=
"qpdf --decrypt '
$backup
' '
$file
' 2>&1"
;
// Try to decrypt file
exec
(
$cmdQpdf
,
$outputQpdf
,
$rcQpdf
);
if
(
$rcQpdf
!=
0
)
{
// qpdf failed: restore origfile in case the $file has been destroyed.
HelperFile
::
copy
(
$backup
,
$file
);
throw
new
\
DownloadException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
"Failed to decrypt PDF"
,
ERROR_MESSAGE_TO_DEVELOPER
=>
"CMD: "
.
$cmdQpdf
.
"<br>RC:
$rc
<br>Output: "
.
implode
(
"<br>"
,
$outputQpdf
)])
,
ERROR_DOWNLOAD_MERGE_FAILED
);
}
}
}
else
{
throw
new
\
DownloadException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
"Merge PDF file failed."
,
ERROR_MESSAGE_TO_DEVELOPER
=>
"CMD: "
.
$cmd
.
"<br>RC:
$rc
<br>Output: "
.
implode
(
"<br>"
,
$rcOutput
)])
,
ERROR_DOWNLOAD_MERGE_FAILED
);
}
}
return
$rc
;
}
/**
* Get the mimetype of $filename and store them in $rcMimetype.
* Checks if the extension of $outputFilename fit's to the mimetype. If not, append the mimetype extension.
...
...
@@ -168,7 +230,8 @@ class Download {
*
* @return string possible updated $outputFilename, according the mimetype.
*/
private
function
targetFilenameExtension
(
$filename
,
$outputFilename
,
&
$rcMimetype
)
{
private
function
targetFilenameExtension
(
$filename
,
$outputFilename
,
&
$rcMimetype
)
{
$rcMimetype
=
mime_content_type
(
$filename
);
...
...
@@ -182,7 +245,8 @@ class Download {
* @param $outputFilename
* @throws \DownloadException
*/
private
function
outputFile
(
$file
,
$outputFilename
)
{
private
function
outputFile
(
$file
,
$outputFilename
)
{
$json
=
''
;
$flagJson
=
(
$this
->
getOutputFormat
()
===
DOWNLOAD_OUTPUT_FORMAT_JSON
);
...
...
@@ -237,7 +301,8 @@ class Download {
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
private
function
getElement
(
$element
,
$downloadMode
,
&
$rcData
)
{
private
function
getElement
(
$element
,
$downloadMode
,
&
$rcData
)
{
$filename
=
''
;
$rcArgs
=
array
();
...
...
@@ -323,7 +388,8 @@ class Download {
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
private
function
getEvaluatedBodyText
(
$uid
,
$urlParam
)
{
private
function
getEvaluatedBodyText
(
$uid
,
$urlParam
)
{
foreach
(
$urlParam
as
$key
=>
$paramValue
)
{
$this
->
store
->
setVar
(
$key
,
$paramValue
,
STORE_SIP
);
}
...
...
@@ -346,7 +412,8 @@ class Download {
* @return string ZIP filename - has to be deleted later.
* @throws \DownloadException
*/
private
function
zipFiles
(
array
$files
)
{
private
function
zipFiles
(
array
$files
)
{
$zipFile
=
HelperFile
::
tempnam
();
if
(
false
===
$zipFile
)
{
...
...
@@ -398,7 +465,8 @@ class Download {
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
private
function
doElements
(
array
$vars
,
$outputMode
)
{
private
function
doElements
(
array
$vars
,
$outputMode
)
{
$tmpFiles
=
array
();
...
...
@@ -519,7 +587,8 @@ class Download {
* @throws \UserFormException
* @throws \UserReportException
*/
private
function
doThumbnail
(
$urlParam
)
{
private
function
doThumbnail
(
$urlParam
)
{
$thumbnail
=
new
Thumbnail
();
$pathFilenameThumbnail
=
$thumbnail
->
process
(
$urlParam
,
THUMBNAIL_VIA_DOWNLOAD
);
...
...
@@ -543,7 +612,8 @@ class Download {
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public
function
process
(
$vars
,
$outputMode
=
OUTPUT_MODE_DIRECT
)
{
public
function
process
(
$vars
,
$outputMode
=
OUTPUT_MODE_DIRECT
)
{
if
(
!
is_array
(
$vars
))
{
$vars
=
$this
->
store
->
getStore
(
STORE_SIP
);
...
...
@@ -557,14 +627,16 @@ class Download {
/**
* @param $outputFormat
*/
private
function
setOutputFormat
(
$outputFormat
)
{
private
function
setOutputFormat
(
$outputFormat
)
{
$this
->
outputFormat
=
$outputFormat
;
}
/**
* @return string - DOWNLOAD_OUTPUT_FORMAT_RAW | DOWNLOAD_OUTPUT_FORMAT_JSON
*/
public
function
getOutputFormat
()
{
public
function
getOutputFormat
()
{
return
$this
->
outputFormat
;
}
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment