requireLogin(); $id = trim((string) ($_GET['id'] ?? '')); $field = trim((string) ($_GET['field'] ?? '')); $index = (int) ($_GET['index'] ?? -1); $store = new JsonStore(); $submission = $store->getSubmissionByKey($id); if ($submission === null) { http_response_code(404); echo 'Antrag nicht gefunden.'; exit; } $entry = $submission['uploads'][$field][$index] ?? null; if (!is_array($entry)) { http_response_code(404); echo 'Datei nicht gefunden.'; exit; } $app = Bootstrap::config('app'); $base = rtrim((string) $app['storage']['uploads'], '/'); $relativePath = (string) ($entry['relative_path'] ?? ''); $relativePath = str_replace(['..', '\\'], '', $relativePath); $fullPath = $base . '/' . ltrim($relativePath, '/'); if (!is_file($fullPath)) { http_response_code(404); echo 'Datei nicht vorhanden.'; exit; } $downloadName = (string) ($entry['original_filename'] ?? basename($fullPath)); $downloadName = str_replace(["\r", "\n"], '', $downloadName); $fallbackName = preg_replace('/[^A-Za-z0-9._-]/', '_', $downloadName) ?: 'download.bin'; $encodedName = rawurlencode($downloadName); header('Content-Type: ' . ((string) ($entry['mime'] ?? 'application/octet-stream'))); header('Content-Length: ' . (string) filesize($fullPath)); header('Content-Disposition: attachment; filename="' . $fallbackName . '"; filename*=UTF-8\'\'' . $encodedName); readfile($fullPath); exit;