bericht/api/photo.php
Eduard Wisch 606ffae1fe
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
fix: photo.php eigener Init ohne _inc.php JSON-Header
_inc.php setzt standardmäßig Content-Type: application/json — das
hat photo.php beim Bildversand blockiert (die Blob kam als JSON rein
und der Browser konnte sie nicht als Bild darstellen).

Jetzt lädt photo.php Dolibarr direkt, dekodiert JWT manuell (mit
Query-Param-Support für Fallback ohne Header-Auth) und sendet NUR
image/jpeg-Header beim Ausliefern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 23:25:24 +02:00

108 lines
4 KiB
PHP

<?php
/* GET /api/photo.php?relpath=<path>
* 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 <name>_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 <img src> 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;