* Liefert eine Datei aus DOL_DATA_ROOT aus, authentifiziert per JWT. * Whitelist: nur facture/, commande/, propal/, bericht/ * * Für den Thumb-Request der PWA werden auch Thumbnails ausgeliefert * (Dolibarr legt _small.png unter thumbs/ ab). * * Query: * relpath — relativer Pfad unter DOL_DATA_ROOT * size=small|mini (optional, nutzt automatisch das Thumb) */ // Dieser Endpoint liefert Binärdaten aus — KEIN JSON Content-Type! // Deshalb nicht _inc.php direkt nutzen, sondern JWT + Dolibarr manuell laden. if (!defined('NOLOGIN')) define('NOLOGIN', '1'); if (!defined('NOCSRFCHECK')) define('NOCSRFCHECK', '1'); if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); if (!defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1'); if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); $res = 0; if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; $tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1; while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; } if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php"; if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php"; if (!$res) die("Include of main fails"); require_once __DIR__.'/_jwt.php'; require_once __DIR__.'/../lib/bericht.lib.php'; // Support Token via Header ODER Query-String (für ohne Header) $token_str = ''; $hdr = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if ($hdr && stripos($hdr, 'bearer ') === 0) $token_str = trim(substr($hdr, 7)); if (!$token_str && !empty($_GET['jwt'])) $token_str = (string) $_GET['jwt']; $payload = $token_str ? bericht_jwt_decode($token_str) : null; if (!$payload || empty($payload['sub'])) { http_response_code(401); header('Content-Type: text/plain'); echo 'Token ungültig oder fehlt'; exit; } require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; $u = new User($db); if ($u->fetch((int) $payload['sub']) <= 0 || empty($u->statut)) { http_response_code(401); header('Content-Type: text/plain'); echo 'User ungültig'; exit; } $u->loadRights(); if (!$u->hasRight('bericht', 'read')) { http_response_code(403); header('Content-Type: text/plain'); echo 'Permission denied'; exit; } $user = $u; $relpath = (string) ($_GET['relpath'] ?? ''); $size = (string) ($_GET['size'] ?? ''); if (empty($relpath)) { http_response_code(400); header('Content-Type: text/plain'); echo 'relpath fehlt'; exit; } // Whitelist if (!preg_match('#^(facture|commande|propal|bericht)/#', $relpath)) { http_response_code(403); header('Content-Type: text/plain'); echo 'Pfad nicht erlaubt: '.$relpath; exit; } $full = bericht_resolve_data_path($relpath); if (!$full || !file_exists($full)) { http_response_code(404); header('Content-Type: text/plain'); echo 'Datei nicht gefunden: '.$relpath; exit; } // Thumb-Variante if ($size === 'small' || $size === 'mini') { $dir = dirname($full); $base = pathinfo($full, PATHINFO_FILENAME); $ext = pathinfo($full, PATHINFO_EXTENSION); $thumb = $dir.'/thumbs/'.$base.'_'.$size.'.'.$ext; if (file_exists($thumb)) $full = $thumb; } $mime = function_exists('dol_mimetype') ? dol_mimetype($full) : 'application/octet-stream'; header('Content-Type: '.$mime); header('Content-Length: '.filesize($full)); header('Cache-Control: private, max-age=3600'); header('Access-Control-Allow-Origin: *'); readfile($full); exit;