Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
.vscode
.phpunit.result.cache
vendor
/vendor-bin/**/vendor/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ With JSignPDF bin:
```php
$param->setjSignPdfJarPath('/path/to/jsignpdf');
```
With specific Java or JSignPdf version:
```php
$params->getJSignPdfDownloadUrl('the url to download the zip here');
$params->setJavaDownloadUrl('the url to download the .tar.gz here');
```

Without JSignPDF bin:
```bash
Expand Down
28 changes: 23 additions & 5 deletions example/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,34 @@
/**
* @author Jeidison Farias <jeidison.farias@gmail.com>
*/
require_once "../src/JSignPDF.php";
require_once __DIR__ . '/../vendor/autoload.php';

use Jeidison\JSignPDF\JSignPDF;
use Jeidison\JSignPDF\Sign\JSignParam;

$password = '123';

$privateKey = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);

$csr = openssl_csr_new(['commonName' => 'John Doe'], $privateKey, ['digest_alg' => 'sha256']);
$x509 = openssl_csr_sign($csr, null, $privateKey, 365);

openssl_pkcs12_export(
$x509,
$pfxCertificateContent,
$privateKey,
$password,
);

$param = JSignParam::instance();
$param->setCertificate(file_get_contents('../tests/resources/certificado.pfx'));
$param->setPdf(file_get_contents('../tests/resources/pdf-test.pdf'));
$param->setPassword('123');

$param->setCertificate($pfxCertificateContent);
$param->setPdf(file_get_contents(__DIR__ . '/../tests/resources/pdf-test.pdf'));
$param->setPassword($password);

$jSignPdf = new JSignPDF($param);
$fileSigned = $jSignPdf->sign();
file_put_contents('../tmp/file_signed.pdf', $fileSigned);
file_put_contents(__DIR__ . '/../tmp/file_signed.pdf', $fileSigned);
4 changes: 2 additions & 2 deletions src/JSignPDF.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ class JSignPDF
private $service;
private $param;

public function __construct(JSignParam $param = null)
public function __construct(?JSignParam $param = null)
{
$this->service = new JSignService();
$this->param = $param;
}

public static function instance(JSignParam $param = null)
public static function instance(?JSignParam $param = null)
{
return new self($param);
}
Expand Down
105 changes: 105 additions & 0 deletions src/Runtime/JSignPdfRuntimeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace Jeidison\JSignPDF\Runtime;

use InvalidArgumentException;
use Jeidison\JSignPDF\Sign\JSignParam;
use RuntimeException;
use ZipArchive;

class JSignPdfRuntimeService
{
public function getPath(JSignParam $params): string
{
$jsignPdfPath = $params->getjSignPdfJarPath();
$downloadUrl = $params->getJSignPdfDownloadUrl();

if ($jsignPdfPath && !$downloadUrl) {
if (file_exists($jsignPdfPath)) {
return $jsignPdfPath;
}
throw new InvalidArgumentException('Jar of JSignPDF not found on path: '. $jsignPdfPath);
}

if ($downloadUrl && $jsignPdfPath) {
$baseDir = preg_replace('/\/JSignPdf.jar$/', '', $jsignPdfPath);
if (!is_dir($baseDir)) {
$ok = mkdir($baseDir, 0755, true);
if ($ok === false) {
throw new InvalidArgumentException('The JSignPdf base dir cannot be created: '. $baseDir);
}
}
if (!file_exists($jsignPdfPath) || !self::validateVersion($params)) {
self::downloadAndExtract($params);
}
return $jsignPdfPath;
}

throw new InvalidArgumentException('Java not found.');
}

private function validateVersion(JSignParam $params): bool
{
$jsignPdfPath = $params->getjSignPdfJarPath();
$versionCacheFile = $jsignPdfPath . '/.jsignpdf_version_' . basename($params->getJSignPdfDownloadUrl());
return file_exists($versionCacheFile);
}

private function downloadAndExtract(JSignParam $params): void
{
$jsignPdfPath = $params->getjSignPdfJarPath();
$url = $params->getJSignPdfDownloadUrl();

$baseDir = preg_replace('/\/JSignPdf.jar$/', '', $jsignPdfPath);

if (!is_dir($baseDir)) {
$ok = mkdir($baseDir, 0755, true);
if (!$ok) {
throw new RuntimeException('Failure to create the folder: ' . $baseDir);
}
}
if (!filter_var($url, FILTER_VALIDATE_URL)) {
throw new InvalidArgumentException('The url to download Java is invalid: ' . $url);
}
$this->chunkDownload($url, $baseDir . '/jsignpdf.zip');
$z = new ZipArchive();
$ok = $z->open($baseDir . '/jsignpdf.zip');
if ($ok !== true) {
throw new InvalidArgumentException('The file ' . $baseDir . '/jsignpdf.zip cannot be extracted');
}
$ok = $z->extractTo(pathto: $baseDir, files: [$z->getNameIndex(0) . 'JSignPdf.jar']);
if ($ok !== true) {
throw new InvalidArgumentException('JSignPdf.jar not found inside path: ' . $z->getNameIndex(0) . 'JSignPdf.jar');
}
@exec('mv ' . escapeshellarg($baseDir . '/'. $z->getNameIndex(0)) . '/JSignPdf.jar ' . escapeshellarg($baseDir));
@exec('rm -rf ' . escapeshellarg($baseDir . '/'. $z->getNameIndex(0)));
@exec('rm -f ' . escapeshellarg($baseDir) . '/.jsignpdf_version_*');
unlink($baseDir . '/jsignpdf.zip');
if (!file_exists($baseDir . '/JSignPdf.jar')) {
throw new RuntimeException('Java binary not found at: ' . $baseDir . '/bin/java');
}
touch($baseDir . '/.jsignpdf_version_' . basename($url));
}

private function chunkDownload(string $url, string $destination): void
{
$fp = fopen($destination, 'w');

if ($fp) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
if ($response === false) {
throw new InvalidArgumentException('Failure to download file using the url ' . $url);
}
curl_close($ch);
fclose($fp);
} else {
throw new InvalidArgumentException("Failute to download file using the url $url");
}
}
}
128 changes: 128 additions & 0 deletions src/Runtime/JavaRuntimeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

declare(strict_types=1);

namespace Jeidison\JSignPDF\Runtime;

use InvalidArgumentException;
use Jeidison\JSignPDF\Sign\JSignParam;
use PharData;
use PharException;
use RuntimeException;
use UnexpectedValueException;

class JavaRuntimeService
{
public function getPath(JSignParam $params): string
{
if ($params->isUseJavaInstalled()) {
return 'java';
}

$javaPath = $params->getJavaPath();
$downloadUrl = $params->getJavaDownloadUrl();

if ($javaPath && !$downloadUrl) {
if (is_file($javaPath) && is_executable($javaPath)) {
return $javaPath;
}
throw new InvalidArgumentException('Java path defined is not executable: ' . $javaPath);
}

if ($downloadUrl && $javaPath) {
$baseDir = preg_replace('/\/bin\/java$/', '', $javaPath);
if (!is_dir($baseDir)) {
$ok = mkdir($baseDir, 0755, true);
if ($ok === false) {
throw new InvalidArgumentException('The java base dir is not a real directory. Create this directory first: '. $baseDir);
}
}
if (!self::validateVersion($params)) {
self::downloadAndExtract($downloadUrl, $javaPath);
}
$params->setJavaDownloadUrl('');
$params->setJavaPath($javaPath);
return $javaPath;
}

throw new InvalidArgumentException('Java not found.');
}

private function validateVersion(JSignParam $params): bool
{
$javaPath = $params->getJavaPath();
$baseDir = preg_replace('/\/bin\/java$/', '', $javaPath);
$lastVersion = $baseDir . '/.java_version_' . basename($params->getJavaDownloadUrl());
return file_exists($lastVersion);
}

private function downloadAndExtract(string $url, string $baseDir): void
{
$baseDir = preg_replace('/\/bin\/java$/', '', $baseDir);

if (!is_dir($baseDir)) {
$ok = mkdir($baseDir, 0755, true);
if (!$ok) {
throw new RuntimeException('Failure to create the folder: ' . $baseDir);
}
}
if (!filter_var($url, FILTER_VALIDATE_URL)) {
throw new InvalidArgumentException('The url to download Java is invalid: ' . $url);
}
$this->chunkDownload($url, $baseDir . '/java.tar.gz');
try {
$tar = new PharData($baseDir . '/java.tar.gz');
} catch (PharException|UnexpectedValueException $e) {
throw new InvalidArgumentException('The file ' . $baseDir . '/java.tar.gz cannot be extracted');
}
$rootDirInsideTar = $this->findRootDir($tar, $baseDir . '/java.tar.gz');
if (!$rootDirInsideTar) {
throw new InvalidArgumentException('Invalid tar content.');
}
$tar->extractTo(directory: $baseDir, overwrite: true);
@exec('mv ' . escapeshellarg($baseDir . '/'. $rootDirInsideTar) . '/* ' . escapeshellarg($baseDir));
@exec('rm -rf ' . escapeshellarg($baseDir . '/'. $rootDirInsideTar));
@exec('rm -f ' . escapeshellarg($baseDir) . '/.java_version_*');
unlink($baseDir . '/java.tar.gz');
touch($baseDir . '/.java_version_' . basename($url));
if (!file_exists($baseDir . '/bin/java')) {
throw new RuntimeException('Java binary not found at: ' . $baseDir . '/bin/java');
}
chmod($baseDir . '/bin/java', 0700);
}

private function findRootDir(PharData $phar, $rootDir) {
$files = new \RecursiveIteratorIterator($phar, \RecursiveIteratorIterator::CHILD_FIRST);
$rootDir = realpath($rootDir);

foreach ($files as $file) {
$pathName = $file->getPathname();
if (str_contains($pathName, '/bin/') || str_contains($pathName, '/bin/')) {
$parts = explode($rootDir, $pathName);
$internalFullPath = end($parts);
$parts = explode('/bin/', $internalFullPath);
return trim($parts[0], '/');
}
}
}

private function chunkDownload(string $url, string $destination): void
{
$fp = fopen($destination, 'w');

if ($fp) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
if ($response === false) {
throw new InvalidArgumentException('Failure to download file using the url ' . $url);
}
curl_close($ch);
fclose($fp);
} else {
throw new InvalidArgumentException("Failute to download file using the url $url");
}
}
}
29 changes: 27 additions & 2 deletions src/Sign/JSignParam.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ class JSignParam
private $pathPdfSigned;
private $JSignParameters = "-a -kst PKCS12";
private $isUseJavaInstalled = false;
private $javaPath = '';
private string $javaPath = '';
private $tempPath;
private $tempName;
private $isOutputTypeBase64 = false;
private $jSignPdfJarPath;
private string $jSignPdfJarPath;
private string $javaDownloadUrl = 'https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.8%2B9/OpenJDK21U-jre_x64_linux_hotspot_21.0.8_9.tar.gz';
private string $jSignPdfDownloadUrl = 'https://github.com/intoolswetrust/jsignpdf/releases/download/JSignPdf_2_3_0/jsignpdf-2.3.0.zip';

public function __construct()
{
$this->tempName = md5(time() . uniqid() . mt_rand());
$this->tempPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
$this->javaPath = $this->tempPath . 'java' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'java';
$this->jSignPdfJarPath = $this->tempPath . 'jsignpdf' . DIRECTORY_SEPARATOR . 'JSignPdf.jar';
}

public static function instance()
Expand Down Expand Up @@ -158,4 +162,25 @@ public function getTempCertificatePath()
return $this->getTempPath() . $this->getTempName('.pfx');
}

public function setJavaDownloadUrl(string $url): self
{
$this->javaDownloadUrl = $url;
return $this;
}

public function getJavaDownloadUrl(): string
{
return $this->javaDownloadUrl;
}

public function setJSignPdfDownloadUrl(string $url): self
{
$this->jSignPdfDownloadUrl = $url;
return $this;
}

public function getJSignPdfDownloadUrl(): string
{
return $this->jSignPdfDownloadUrl;
}
}
Loading