mirror of
https://github.com/24eme/signaturepdf
synced 2026-03-14 13:55:44 +01:00
share action create admin key stored on server side and localstorage if adminKey found on localStorage, display a small trash icon if clicked, send an ajax request, verify the key, and delete both localstorage and files on the server
250 lines
7.4 KiB
PHP
250 lines
7.4 KiB
PHP
<?php
|
|
|
|
class PDFSignature
|
|
{
|
|
protected $symmetricKey = null;
|
|
protected $pathHash = null;
|
|
protected $hash = null;
|
|
protected $gpg = null;
|
|
protected $toClean = [];
|
|
protected $lockFile = null;
|
|
protected $cacheDecryptFiles = [];
|
|
|
|
public function __construct($pathHash, $symmetricKey = null) {
|
|
$this->symmetricKey = $symmetricKey;
|
|
$this->pathHash = $pathHash;
|
|
$this->hash = basename($this->pathHash);
|
|
$this->gpg = new GPGCryptography($symmetricKey, $pathHash);
|
|
$this->lockFile = $this->pathHash.'/.lock';
|
|
}
|
|
|
|
public function createShare($originalFile, $originFileBaseName, $duration) {
|
|
mkdir($this->pathHash);
|
|
$expireFile = $this->pathHash.".expire";
|
|
file_put_contents($expireFile, $duration);
|
|
touch($expireFile, date_format(date_modify(date_create(), file_get_contents($expireFile)), 'U'));
|
|
rename($originalFile, $this->pathHash.'/original.pdf');
|
|
file_put_contents($this->pathHash.'/filename.txt', $originFileBaseName);
|
|
if($this->symmetricKey) {
|
|
$this->gpg->encrypt();
|
|
}
|
|
}
|
|
|
|
public function createAdminKey()
|
|
{
|
|
$link = $this->gpg->createSymmetricKey(20);
|
|
file_put_contents($this->pathHash.'.admin', $link);
|
|
|
|
return $link;
|
|
}
|
|
|
|
public function verifyEncryption() {
|
|
if(!$this->isEncrypted()) {
|
|
|
|
return true;
|
|
}
|
|
|
|
return file_exists($this->getDecryptFile($this->pathHash."/filename.txt"));
|
|
}
|
|
|
|
public function isEncrypted() {
|
|
return $this->gpg->isEncrypted();
|
|
}
|
|
|
|
public function getDecryptFile($file) {
|
|
if(!$this->isEncrypted()) {
|
|
|
|
return $file;
|
|
}
|
|
$file = preg_replace("/\.gpg$/", "", $file);
|
|
if(file_exists($file)) {
|
|
return $file;
|
|
}
|
|
|
|
if(array_key_exists($file, $this->cacheDecryptFiles)) {
|
|
return $this->cacheDecryptFiles[$file];
|
|
}
|
|
$decryptFile = $this->gpg->decryptFile($file);
|
|
$this->toClean[] = $decryptFile;
|
|
$this->cacheDecryptFiles[$file] = $decryptFile;
|
|
|
|
return $decryptFile;
|
|
}
|
|
|
|
public function getPDF() {
|
|
$this->compile();
|
|
return $this->getDecryptFile($this->pathHash.'/final.pdf');
|
|
}
|
|
|
|
public function needToCompile() {
|
|
$needToCompile = false;
|
|
foreach($this->getLayers() as $layerFile) {
|
|
if(!file_exists(str_replace('.svg.pdf', '.sign.pdf', $layerFile))) {
|
|
$needToCompile = true;
|
|
}
|
|
}
|
|
if(!$this->isEncrypted() && !file_exists($this->pathHash.'/final.pdf')) {
|
|
$needToCompile = true;
|
|
}
|
|
if($this->isEncrypted() && !file_exists($this->pathHash.'/final.pdf.gpg')) {
|
|
$needToCompile = true;
|
|
}
|
|
|
|
return $needToCompile;
|
|
}
|
|
|
|
protected function isCompileLock() {
|
|
if(file_exists($this->lockFile) && filemtime($this->lockFile) > time() + 30) {
|
|
unlink($this->lockFile);
|
|
}
|
|
|
|
return file_exists($this->lockFile);
|
|
}
|
|
|
|
protected function lockCompile() {
|
|
touch($this->lockFile);
|
|
}
|
|
|
|
protected function unlockCompile() {
|
|
unlink($this->lockFile);
|
|
}
|
|
|
|
public function compile() {
|
|
if(!$this->needToCompile()) {
|
|
return;
|
|
}
|
|
|
|
if($this->isCompileLock()) {
|
|
return $this->compile();
|
|
}
|
|
|
|
$this->lockCompile();
|
|
|
|
$layers = $this->getLayers($this->pathHash);
|
|
$currentSignedFile = $this->pathHash.'/original.pdf';
|
|
$signedFileToCopy = [];
|
|
foreach($layers as $layerFile) {
|
|
$signedFile = str_replace('.svg.pdf', '.sign.pdf', $layerFile);
|
|
if(!file_exists($signedFile)) {
|
|
$signedFile = preg_replace("/\.gpg$/", '', $signedFile);
|
|
self::addSvgToPDF($this->getDecryptFile($currentSignedFile), $this->getDecryptFile($layerFile), $signedFile, false);
|
|
}
|
|
$currentSignedFile = $signedFile;
|
|
}
|
|
copy($this->getDecryptFile($currentSignedFile), $this->pathHash.'/final.pdf');
|
|
|
|
if($this->isEncrypted()) {
|
|
$this->gpg->encrypt();
|
|
}
|
|
|
|
$this->unlockCompile();
|
|
}
|
|
|
|
public function getPublicFilename() {
|
|
$filename = $this->hash.'.pdf';
|
|
|
|
$file = $this->getDecryptFile($this->pathHash."/filename.txt");
|
|
|
|
if(file_exists($file)) {
|
|
$filename = file_get_contents($file);
|
|
}
|
|
|
|
$filename = str_replace('.pdf', '_signe-'.count($this->getLayers()).'x.pdf', $filename);
|
|
|
|
return $filename;
|
|
}
|
|
|
|
public function getLayers($pathHash = null) {
|
|
if(is_null($pathHash)) {
|
|
$pathHash = $this->pathHash;
|
|
}
|
|
if(!file_exists($pathHash)) {
|
|
return [];
|
|
}
|
|
$files = scandir($pathHash);
|
|
$layers = [];
|
|
foreach($files as $file) {
|
|
if(strpos($file, '.svg.pdf') !== false) {
|
|
$layers[] = $pathHash.'/'.$file;
|
|
}
|
|
}
|
|
return $layers;
|
|
}
|
|
|
|
public function addSignature(array $svgFiles) {
|
|
$expireFile = $this->pathHash.".expire";
|
|
touch($expireFile, date_format(date_modify(date_create(), file_get_contents($expireFile)), 'U'));
|
|
|
|
do {
|
|
if(isset($svgPDFFile)) { usleep(1); }
|
|
$svgPDFFile = $this->pathHash."/".(new DateTime())->format('YmdHisu').'.svg.pdf';
|
|
} while (file_exists($svgPDFFile));
|
|
|
|
self::createPDFFromSvg($svgFiles, $svgPDFFile);
|
|
|
|
if($this->isEncrypted()) {
|
|
$this->gpg->encrypt();
|
|
}
|
|
$this->toClean = array_merge($this->toClean, $svgFiles);
|
|
$this->compile();
|
|
}
|
|
|
|
public static function createPDFFromSvg(array $svgFiles, $outputPdfFile) {
|
|
shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $outputPdfFile, implode(" ", $svgFiles)));
|
|
}
|
|
|
|
public static function addSvgToPDF($pdfOrigin, $pdfSvg, $pdfOutput, $digitalSignature = true) {
|
|
shell_exec(sprintf("pdftk %s multistamp %s output %s", $pdfOrigin, $pdfSvg, $pdfOutput));
|
|
if (NSSCryptography::getInstance()->isEnabled() && $digitalSignature) {
|
|
try {
|
|
NSSCryptography::getInstance()->addSignature($pdfOutput, 'Signed with SignaturePDF');
|
|
} catch(Exception $e) {
|
|
error_log($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function flatten($pdf)
|
|
{
|
|
// check version of imagick
|
|
$command = (null === shell_exec("command -v magick")) ? 'convert' : 'magick';
|
|
|
|
shell_exec(sprintf(
|
|
'%s -density 200 -units PixelsPerInch %s_signe.pdf -compress zip %s_signe.pdf'
|
|
, $command, escapeshellarg($pdf), escapeshellarg($pdf)));
|
|
}
|
|
|
|
public function clean() {
|
|
foreach($this->toClean as $path) {
|
|
if(strpos($path, $this->pathHash) !== false) {
|
|
continue;
|
|
}
|
|
GPGCryptography::hardUnlink($path);
|
|
}
|
|
}
|
|
|
|
public static function isrsvgConvertInstalled() {
|
|
$output = null;
|
|
$returnCode = null;
|
|
|
|
exec('rsvg-convert --version', $output, $returnCode);
|
|
|
|
if (!$output) {
|
|
return array(false);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
public static function ispdftkInstalled() {
|
|
$output = null;
|
|
$returnCode = null;
|
|
|
|
exec('pdftk --version', $output, $returnCode);
|
|
|
|
if (!$output) {
|
|
return array(false);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
}
|