fix: photo.php eigener Init ohne _inc.php JSON-Header
All checks were successful
Deploy bericht / deploy (push) Successful in 1s

_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]
This commit is contained in:
Eduard Wisch 2026-04-08 23:25:24 +02:00
parent 95701ed2d6
commit 606ffae1fe

View file

@ -10,40 +10,96 @@
* relpath relativer Pfad unter DOL_DATA_ROOT
* size=small|mini (optional, nutzt automatisch das Thumb)
*/
require_once __DIR__.'/_inc.php';
// 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');
api_authenticate();
global $db, $user;
$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)) api_fail('relpath fehlt');
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)) {
api_fail('Pfad nicht erlaubt', 403);
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)) api_fail('Datei nicht gefunden', 404);
if (!$full || !file_exists($full)) {
http_response_code(404);
header('Content-Type: text/plain');
echo 'Datei nicht gefunden: '.$relpath;
exit;
}
// Thumb-Variante (kleiner, schneller)
// 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;
}
if (file_exists($thumb)) $full = $thumb;
}
// Content-Type aus mimetype
$mime = function_exists('dol_mimetype') ? dol_mimetype($full) : 'application/octet-stream';
// Binary ausliefern — header() ersetzt vorherigen JSON Content-Type
header_remove('Content-Type');
header('Content-Type: '.$mime);
header('Content-Length: '.filesize($full));
header('Cache-Control: private, max-age=3600');