Skip to content

Commit 4b1b252

Browse files
committed
Start to implement jeidison/pdf-signe
Signed-off-by: Vitor Mattos <vitor@php.rio>
1 parent 71df343 commit 4b1b252

File tree

6 files changed

+229
-2
lines changed

6 files changed

+229
-2
lines changed

lib/Handler/PhpNativeHandler.php

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2023 Vitor Mattos <vitor@php.rio>
6+
*
7+
* @author Vitor Mattos <vitor@php.rio>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*/
24+
25+
namespace OCA\Libresign\Handler;
26+
27+
use Jeidison\JSignPDF\JSignPDF;
28+
use Jeidison\JSignPDF\Sign\JSignParam;
29+
use OCA\Libresign\Exception\LibresignException;
30+
use OCP\AppFramework\Services\IAppConfig;
31+
use Psr\Log\LoggerInterface;
32+
33+
class PhpNativeHandler extends SignEngineHandler {
34+
/** @var JSignPDF */
35+
private $jSignPdf;
36+
/** @var JSignParam */
37+
private $jSignParam;
38+
public const VERSION = '2.2.2';
39+
40+
public function __construct(
41+
private IAppConfig $appConfig,
42+
private LoggerInterface $logger,
43+
) {
44+
}
45+
46+
public function setJSignPdf(JSignPDF $jSignPdf): void {
47+
$this->jSignPdf = $jSignPdf;
48+
}
49+
50+
public function getJSignPdf(): JSignPDF {
51+
if (!$this->jSignPdf) {
52+
// @codeCoverageIgnoreStart
53+
$this->setJSignPdf(new JSignPDF());
54+
// @codeCoverageIgnoreEnd
55+
}
56+
return $this->jSignPdf;
57+
}
58+
59+
/**
60+
* @psalm-suppress MixedReturnStatement
61+
*/
62+
public function getJSignParam(): JSignParam {
63+
if (!$this->jSignParam) {
64+
$javaPath = $this->appConfig->getAppValue('java_path');
65+
$this->jSignParam = (new JSignParam())
66+
->setTempPath(
67+
$this->appConfig->getAppValue('jsignpdf_temp_path', sys_get_temp_dir() . DIRECTORY_SEPARATOR)
68+
)
69+
->setIsUseJavaInstalled(empty($javaPath))
70+
->setjSignPdfJarPath(
71+
$this->appConfig->getAppValue('jsignpdf_jar_path', '/opt/jsignpdf-' . self::VERSION . '/JSignPdf.jar')
72+
);
73+
if (!empty($javaPath)) {
74+
if (!file_exists($javaPath)) {
75+
throw new \Exception('Invalid Java binary. Run occ libresign:install --java');
76+
}
77+
$this->jSignParam->setJavaPath($javaPath);
78+
}
79+
}
80+
return $this->jSignParam;
81+
}
82+
83+
/**
84+
* @psalm-suppress MixedReturnStatement
85+
*/
86+
public function sign(): string {
87+
$param = $this->getJSignParam()
88+
->setCertificate($this->getCertificate())
89+
->setPdf($this->getInputFile()->getContent())
90+
->setPassword($this->getPassword());
91+
92+
$signed = $this->signUsingVisibleElements();
93+
if ($signed) {
94+
return $signed;
95+
}
96+
$jSignPdf = $this->getJSignPdf();
97+
$jSignPdf->setParam($param);
98+
return $this->signWrapper($jSignPdf);
99+
}
100+
101+
private function signUsingVisibleElements(): string {
102+
$visibleElements = $this->getvisibleElements();
103+
if ($visibleElements) {
104+
$jSignPdf = $this->getJSignPdf();
105+
$param = $this->getJSignParam();
106+
foreach ($visibleElements as $element) {
107+
$param
108+
->setJSignParameters(
109+
$param->getJSignParameters() .
110+
' -pg ' . $element->getFileElement()->getPage() .
111+
' -llx ' . $element->getFileElement()->getLlx() .
112+
' -lly ' . $element->getFileElement()->getLly() .
113+
' -urx ' . $element->getFileElement()->getUrx() .
114+
' -ury ' . $element->getFileElement()->getUry() .
115+
' --l2-text ""' .
116+
' -V' .
117+
' --bg-path ' . $element->getTempFile()
118+
);
119+
$jSignPdf->setParam($param);
120+
$signed = $this->signWrapper($jSignPdf);
121+
}
122+
return $signed;
123+
}
124+
return '';
125+
}
126+
127+
private function signWrapper(JSignPDF $jSignPDF): string {
128+
try {
129+
return $jSignPDF->sign();
130+
} catch (\Throwable $th) {
131+
$rows = str_getcsv($th->getMessage());
132+
$hashAlgorithm = array_filter($rows, fn ($r) => str_contains($r, 'The chosen hash algorithm'));
133+
if (!empty($hashAlgorithm)) {
134+
$hashAlgorithm = current($hashAlgorithm);
135+
$hashAlgorithm = trim($hashAlgorithm, 'INFO ');
136+
$hashAlgorithm = str_replace('\"', '"', $hashAlgorithm);
137+
$hashAlgorithm = preg_replace('/\.( )/', ".\n", $hashAlgorithm);
138+
throw new LibresignException($hashAlgorithm);
139+
}
140+
$this->logger->error('Error at JSignPdf side. LibreSign can not do nothing. Follow the error message: ' . $th->getMessage());
141+
throw new \Exception($th->getMessage());
142+
}
143+
}
144+
}

lib/Handler/Pkcs12Handler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ public function getPfx(?string $uid = null): string {
133133
}
134134

135135
private function getHandler(): SignEngineHandler {
136-
$sign_engine = $this->appConfig->getAppValue('sign_engine', 'JSignPdf');
137-
$property = lcfirst($sign_engine) . 'Handler';
136+
$signature_engine = $this->appConfig->getAppValue('signature_engine', 'JSignPdf');
137+
$property = lcfirst($signature_engine) . 'Handler';
138138
if (!property_exists($this, $property)) {
139139
throw new LibresignException($this->l10n->t('Invalid Sign engine.'), 400);
140140
}

lib/Service/Install/ConfigureCheckService.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ public function checkSign(): array {
7070
* @return ConfigureCheckHelper[]
7171
*/
7272
public function checkJSignPdf(): array {
73+
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
74+
if ($signatureEngine !== 'jsignpdf') {
75+
return [];
76+
}
7377
$jsignpdJarPath = $this->appConfig->getAppValue('jsignpdf_jar_path');
7478
if ($jsignpdJarPath) {
7579
if (file_exists($jsignpdJarPath)) {
@@ -182,6 +186,10 @@ public function checkPdftk(): array {
182186
* @return ConfigureCheckHelper[]
183187
*/
184188
private function checkJava(): array {
189+
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
190+
if ($signatureEngine !== 'jsignpdf') {
191+
return [];
192+
}
185193
$javaPath = $this->appConfig->getAppValue('java_path');
186194
if ($javaPath) {
187195
if (file_exists($javaPath)) {

lib/Service/Install/InstallService.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ public function setResource(string $resource): self {
324324
}
325325

326326
public function installJava(?bool $async = false): void {
327+
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
328+
if ($signatureEngine !== 'jsignpdf') {
329+
return [];
330+
}
327331
$this->setResource('java');
328332
if ($async) {
329333
$this->runAsync();
@@ -394,6 +398,10 @@ public function uninstallJava(): void {
394398
}
395399

396400
public function installJSignPdf(?bool $async = false): void {
401+
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
402+
if ($signatureEngine !== 'jsignpdf') {
403+
return [];
404+
}
397405
if (!extension_loaded('zip')) {
398406
throw new RuntimeException('Zip extension is not available');
399407
}

src/views/Settings/Settings.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<template>
2525
<NcSettingsSection :name="name">
2626
<CertificateEngine />
27+
<SignatureEngine />
2728
<DownloadBinaries />
2829
<ConfigureCheck />
2930
<RootCertificateCfssl />
@@ -42,6 +43,7 @@
4243
<script>
4344
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
4445
import CertificateEngine from './CertificateEngine.vue'
46+
import SignatureEngine from './SignatureEngine.vue'
4547
import DownloadBinaries from './DownloadBinaries.vue'
4648
import ConfigureCheck from './ConfigureCheck.vue'
4749
import RootCertificateCfssl from './RootCertificateCfssl.vue'
@@ -60,6 +62,7 @@ export default {
6062
components: {
6163
NcSettingsSection,
6264
CertificateEngine,
65+
SignatureEngine,
6366
DownloadBinaries,
6467
ConfigureCheck,
6568
RootCertificateCfssl,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<template>
2+
<NcSettingsSection :name="name" :description="description">
3+
<div class="signature-engine-content">
4+
<NcSelect input-id="signatureEngine"
5+
:aria-label-combobox="description"
6+
:clearable="false"
7+
:value="value"
8+
:options="options"
9+
@input="saveEngine" />
10+
</div>
11+
</NcSettingsSection>
12+
</template>
13+
<script>
14+
import { translate as t } from '@nextcloud/l10n'
15+
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
16+
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
17+
import { emit } from '@nextcloud/event-bus'
18+
import { loadState } from '@nextcloud/initial-state'
19+
20+
export default {
21+
name: 'SignatureEngine',
22+
components: {
23+
NcSettingsSection,
24+
NcSelect,
25+
},
26+
data() {
27+
return {
28+
name: t('libresign', 'Signature engine'),
29+
description: t('libresign', 'Select the signature engine to sign the documents'),
30+
value: [],
31+
options: [
32+
{ id: 'JSignPdf', label: 'JSignPdf' },
33+
{ id: 'PhpNative', label: 'PHP native' },
34+
],
35+
}
36+
},
37+
beforeMount() {
38+
const currentOption = {}
39+
currentOption.id = loadState('libresign', 'signature_engine', 'JSignPdf')
40+
if (currentOption.id === 'JSignPdf') {
41+
currentOption.label = 'JSignPdf'
42+
} else {
43+
currentOption.label = 'PHP native'
44+
}
45+
this.value = [currentOption]
46+
},
47+
methods: {
48+
saveEngine(selected) {
49+
this.value = selected
50+
OCP.AppConfig.setValue('libresign', 'signature_engine', selected.id, {
51+
success() {
52+
emit('libresign:signature-engine:changed', selected.id)
53+
},
54+
})
55+
},
56+
},
57+
}
58+
</script>
59+
<style scoped>
60+
.signature-engine-content{
61+
display: flex;
62+
flex-direction: column;
63+
}
64+
</style>

0 commit comments

Comments
 (0)