Files
elwig-www/www/organic/.php/pdf.php
T

230 lines
9.2 KiB
PHP

<?php
function get_address($array, $from, $to): array {
$address = [];
$postalCode = [];
$city = [];
for ($i = $to; $i >= $from; $i--) {
$el = $array[$i];
if (sizeof($postalCode) > 0) {
if (sizeof($address) === 0) $el = rtrim($el, ", \n\r\t\v\0");
if (strlen($el) === 0) continue;
array_unshift($address, $el);
} else if (preg_match("/^[A-Z0-9.\-]{3,},?$/", $el)) {
array_unshift($postalCode, trim($el, ", \n\r\t\v\0"));
} else {
array_unshift($city, $el);
}
}
return [implode(' ', $address), implode(' ', $postalCode), implode(' ', $city)];
}
function parse_pdf(string $filename, bool $loadUrl = false): array | null {
if (!file_exists($filename))
return null;
if (exec("pdftotext -raw " . escapeshellarg($filename) . " -", $text) === false)
return null;
$text = implode("\n", $text);
exec("pdfsig " . escapeshellarg($filename), $sig);
$sig = implode("\n", $sig);
if ($loadUrl) {
$prefix = "/tmp/pdfimages-" . uniqid();
exec("zbarimg -q --raw $(pdfimages -print-filenames " . escapeshellarg($filename) . " $prefix) | uniq; rm -rf $prefix-*.*", $qrCodes);
} else {
$qrCodes = null;
}
$r = preg_match('@([a-z]{2}) (https://webgate\.ec\.europa\.eu/tracesnt/directory/publication/organic-operator/(.*?)\.pdf) (\d+) / (\d+)@', $text, $matches);
if ($r === 1) {
// TRACES certificate
$data = [];
$parts = preg_split('@\n(I+\.\d+) ([^\n]*)@', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$status = str_replace("\n", '', $parts[0]);
for ($i = 3; $i < sizeof($parts); $i += 3) {
$data[$parts[$i - 2]] = trim($parts[$i]);
}
$lang = $matches[1];
$splitAddr = [
'de' => 'Adresse',
'en' => 'Address',
][$lang];
$splitCountry = [
'de' => 'Land',
'en' => 'Country',
][$lang];
$statusMap = [
'de' => [
'AUSGESTELLT' => 'issued',
],
'en' => [
'ISSUED' => 'issued',
]
][$lang];
$activityMap = [
'de' => [
'Aufbereitung' => 'preparation',
'Ausfuhr' => 'export',
'Einfuhr' => 'import',
'Lagerung' => 'storing',
'Produktion' => 'production',
'Vertrieb' => 'distribution',
'Vertrieb/Inverkehrbringen' => 'distribution_placing_on_the_market',
],
'en' => [
'Distribution' => 'distribution',
'Distribution/Placing on the market' => 'distribution_placing_on_the_market',
'Export' => 'export',
'Import' => 'import',
'Preparation' => 'preparation',
'Production' => 'production',
'Storing' => 'storing',
],
][$lang];
$certUrl = $matches[2];
$certId = $matches[3];
$authorityId = explode('.', $certId)[0];
$operatorId = explode('.', $certId)[1];
$operator = preg_split('@\s+@', trim($data['I.3']));
$p1 = array_search($splitAddr, $operator);
$p2 = array_search($splitCountry, $operator);
$operatorName = trim(implode(' ', array_filter($operator, fn($k,$i) => $i > 0 && $i < $p1, ARRAY_FILTER_USE_BOTH)), ', ');
[$opAddr, $opPostal, $opCity] = get_address($operator, $p1 + 1, $p2 - 1);
$authority = preg_split('@\s+@', trim($data['I.4']));
$until = array_search("($authorityId)", $authority);
$p1 = array_search($splitAddr, $authority);
$p2 = array_search($splitCountry, $authority);
$authorityName = implode(' ', array_filter($authority, fn($k,$i) => $i > 0 && $i < $p1 - 1 && ($i !== $p1 - 2 || !str_starts_with($k, '(')), ARRAY_FILTER_USE_BOTH));
[$aAddr, $aPostal, $aCity] = get_address($authority, $p1 + 1, $p2 - 1);
$activities = [];
foreach (explode("\n", $data['I.5']) as $a) {
$activities[] = $activityMap[trim($a, '• ')];
}
preg_match_all('/\([a-g]\)/', $data['I.6'], $matches, PREG_SET_ORDER);
$products = [];
foreach ($matches as $m) {
$products[] = $m[0];
}
preg_match_all('@\d+/\d+/\d+@', $data['I.8'], $matches, PREG_SET_ORDER);
$valid1 = implode('-', array_reverse(explode('/', $matches[0][0])));
$valid2 = implode('-', array_reverse(explode('/', $matches[1][0])));
$sigs = [];
foreach (array_slice(explode("\nSignature #", $sig), 1) as $s) {
$sData = [];
$sData2 = [];
preg_match_all('/\n {2}- (([^:\n]*): )?([^\n]*)/', $s, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
if (strlen($m[2]) === 0) {
$sData2[] = $m[3];
} else {
$sData[$m[2]] = $m[3];
}
}
$sigs[] = [
'signerCommonName' => $sData['Signer Certificate Common Name'],
'valid' => $sData['Signature Validation'] === 'Signature is Valid.',
'trusted' => $sData['Certificate Validation'] === 'Certificate is Trusted.',
'totalDocument' => in_array('Total document signed', $sData2),
'timestamp' => gmdate('Y-m-d\TH:i:s\Z', strtotime($sData['Signing Time'])),
'type' => $sData['Signature Type'],
'hashAlgorithm' => $sData['Signing Hash Algorithm'],
'signerDistinguishedName' => $sData['Signer full Distinguished Name'],
'fieldName' => $sData['Signature Field Name'],
];
}
return [
'type' => 'traces',
'lang' => $lang,
'id' => $certId,
'status' => $statusMap[$status],
'operator' => [
'id' => $operatorId,
'groupOfOperators' => !str_starts_with($data['I.2'], '☑'),
'name' => $operatorName,
'address' => $opAddr,
'postalCode' => $opPostal,
'city' => $opCity,
'countryCode' => $operator[sizeof($operator) - 1],
],
'authority' => [
'id' => $authorityId,
'name' => $authorityName,
'address' => $aAddr,
'postalCode' => $aPostal,
'city' => $aCity,
'countryCode' => $authority[sizeof($authority) - 1],
],
'activities' => $activities,
'productCategories' => $products,
'validFrom' => $valid1,
'validTo' => $valid2,
'url' => $certUrl,
'digitalSignatures' => $sigs,
'qrCodes' => $qrCodes,
];
}
$isLacon = str_contains($text, "\nLACON GmbH\n") && (str_contains($text, "\nAnlage zum Zertifikat\n"));
if (preg_match('/AT-BIO-[0-9]{3}/', $text, $matches) === 1 || $isLacon) {
$authorityId = $matches[0] ?? ($isLacon ? 'AT-BIO-402' : null);
$certId = null;
$certNr = null;
if (preg_match("/$authorityId\.040-[0-9]{7}\.[0-9]{4}\.[0-9]{3}/", $text, $matches) === 1) {
$certId = $matches[0];
}
if (preg_match_all("/\b[0-9]+([._-])[0-9]+\g{-1}[0-9]+\b/", $text, $matches, PREG_SET_ORDER) !== false) {
foreach ($matches as $m) {
if (strlen($m[0]) > 10 && !str_ends_with($certId, $m[0]))
$certNr = $m[0];
}
}
if ($certId === null && $qrCodes !== null && sizeof($qrCodes) > 0 && str_starts_with($qrCodes[0], 'www.easy-cert.com/')) {
$res = exec("curl -L " . escapeshellarg($qrCodes[0]) . " | grep -A 100 '<table' | grep -B 100 '</table>' | sed 's/<[^>]*>//g;s/\\s\\+/ /g;s/^ //g;s/ $//g;s/\\n//m' | grep 'Certificate number' -A 1");
if ($res !== false && preg_match("/$authorityId\.040-[0-9]{7}\.[0-9]{4}\.[0-9]{3}/", $res, $matches) === 1) {
$certId = $matches[0];
}
}
$operator = ['lfbisNr' => null];
if ($authorityId === 'AT-BIO-301') {
// TODO
} else if ($authorityId === 'AT-BIO-302') {
if (preg_match("/^lfbis[^0-9]*([0-9]{6,7})$/im", $text, $matches) === 1)
$operator['lfbisNr'] = str_pad($matches[1], 7, '0', STR_PAD_LEFT);
} else if ($authorityId === 'AT-BIO-401') {
// TODO
} else if ($authorityId === 'AT-BIO-402') {
if (preg_match("/^Betriebsnummer ([0-9]{6,7})$/im", $text, $matches) === 1)
$operator['lfbisNr'] = str_pad($matches[1], 7, '0', STR_PAD_LEFT);
} else if ($authorityId === 'AT-BIO-902') {
if (preg_match("/ lfbis:([0-9]{6,7})$/im", $text, $matches) === 1)
$operator['lfbisNr'] = str_pad($matches[1], 7, '0', STR_PAD_LEFT);
} else if ($authorityId === 'AT-BIO-903') {
// TODO
}
return [
'type' => $authorityId,
'lang' => 'de',
'id' => $certId,
'nr' => $certNr,
'operator' => $operator,
'authority' => ['id' => $authorityId],
'qrCodes' => $qrCodes,
];
}
return ['type' => 'unknown'];
}