butterfly/front/picture.send.php
Claude 18879bff6d Conformidade KB: sem inc/includes.php, logo.png, limpeza e JSON_HEX_TAG
- front/*.php não incluem mais ../../../inc/includes.php: no GLPI 11 o
  LegacyFileLoadController boota o core antes do script, e o include
  quebra quando o plugin roda do marketplace dir (KB-PLUGIN-028/010).
- logo.png (512x512) criado a partir de plugin.png: o LogoController só
  reconhece o nome fixo logo.png (KB-INFRA-002).
- Removidos front/test_simple.php e front/login.css.php (deprecated),
  incluindo a firewall strategy correspondente no setup.php (KB-018).
- json_encode com JSON_HEX_TAG|JSON_HEX_AMP em todo dado dinâmico
  embutido em <script> (KB-PLUGIN-003).
- .gitignore padrão KB-018; .DS_Store removidos.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:25:56 -03:00

64 lines
1.9 KiB
PHP

<?php
/**
* Lightweight public endpoint to serve plugin pictures.
* Works without authentication and is compatible with GLPI 11 routing.
*/
// GLPI 11: o core já foi bootado pelo LegacyFileLoadController antes deste
// arquivo executar — não incluir inc/includes.php (KB-PLUGIN-028)
$rawPath = $_GET['path'] ?? '';
if ($rawPath === '') {
http_response_code(400);
die('Missing path parameter');
}
$theme = $_GET['theme'] ?? '';
// Basic sanitization while keeping subdirectories (needed for storage layout)
$path = preg_replace('/[^a-zA-Z0-9._\\-\\/]/', '', $rawPath);
$path = str_replace(['../', '..\\'], '', $path);
$theme = preg_replace('/[^a-zA-Z0-9_\\-]/', '', $theme);
if ($path === '') {
http_response_code(400);
die('Invalid path');
}
if ($theme !== '') {
$baseDir = PluginButterflyTheme::getThemeFolder() . '/' . $theme;
} else {
$baseDir = PluginButterflyToolbox::getStorageBasePath();
}
$fullPath = $baseDir . '/' . ltrim($path, '/');
$realBase = is_dir($baseDir) ? realpath($baseDir) : false;
$realPath = realpath($fullPath);
if ($realBase === false || $realPath === false) {
http_response_code(404);
die('File not found');
}
$realBase = rtrim($realBase, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (strpos($realPath, $realBase) !== 0) {
http_response_code(403);
die('Access denied');
}
if (!is_file($realPath) || !is_readable($realPath)) {
http_response_code(404);
die('File not found');
}
$mimeType = mime_content_type($realPath) ?: 'application/octet-stream';
$fileSize = filesize($realPath);
$fileName = basename($realPath);
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . $fileSize);
header('Content-Disposition: inline; filename="' . $fileName . '"');
header('Cache-Control: public, max-age=3600');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($realPath)) . ' GMT');
readfile($realPath);