35 lines
1.7 KiB
PHP
Executable file
35 lines
1.7 KiB
PHP
Executable file
<?php
|
|
// The only independently executable feature endpoint. No portal session needed.
|
|
require_once __DIR__ . '/common.php';
|
|
if (!in_array($_SERVER['REQUEST_METHOD'] ?? '', ['GET', 'HEAD'], true)) {
|
|
header('Allow: GET, HEAD'); http_response_code(405); exit;
|
|
}
|
|
$filesKey = files_string($_GET['files_id'] ?? null);
|
|
if (!preg_match('/^[a-f0-9]{32}$/D', $filesKey)) { http_response_code(404); exit('File not found.'); }
|
|
$filesStream = null;
|
|
try {
|
|
$filesLock = files_lock(LOCK_SH);
|
|
try {
|
|
$filesStore = files_read();
|
|
if (!isset($filesStore['items'][$filesKey])) { http_response_code(404); exit('File not found.'); }
|
|
$filesItem = $filesStore['items'][$filesKey];
|
|
$filesStream = @fopen(__DIR__ . '/data/uploads/' . $filesKey . '.bin', 'rb');
|
|
if (!$filesStream) { http_response_code(404); exit('Stored file is missing.'); }
|
|
$filesStat = fstat($filesStream);
|
|
if (!$filesStat || $filesStat['size'] !== $filesItem['size']) throw new RuntimeException('Stored file size mismatch.');
|
|
} finally { flock($filesLock, LOCK_UN); fclose($filesLock); }
|
|
} catch (Throwable $filesException) {
|
|
if (is_resource($filesStream)) fclose($filesStream);
|
|
http_response_code(503); header('Content-Type: text/plain; charset=UTF-8');
|
|
exit('File storage is unavailable. Check the portal file-sharing section.');
|
|
}
|
|
header('Content-Type: application/octet-stream');
|
|
header('Content-Disposition: attachment; filename="' . $filesItem['name'] . '"');
|
|
header('Content-Length: ' . $filesItem['size']);
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('Cache-Control: no-store');
|
|
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'GET') {
|
|
@set_time_limit(0);
|
|
fpassthru($filesStream);
|
|
}
|
|
fclose($filesStream);
|