<?php
// /elms/download.php — safe download proxy for uploads/assessments ONLY
declare(strict_types=1);

@header('X-Content-Type-Options: nosniff');
@header('Referrer-Policy: no-referrer');
@header("Content-Security-Policy: default-src 'none'; frame-ancestors 'self'; sandbox");

$DEBUG = isset($_GET['debug']) && $_GET['debug'] === '1';
if ($DEBUG) { ini_set('display_errors','1'); error_reporting(E_ALL); }

$docRoot = rtrim((string)($_SERVER['DOCUMENT_ROOT'] ?? ''), '/');
$rootWeb = '/elms';
$baseRel = '/uploads/assessments';
$baseAbs = realpath($docRoot.$rootWeb.$baseRel);

$in = (string)($_GET['p'] ?? '');
$in = str_replace("\0",'', $in);
if ($in === '') { http_response_code(400); exit('Missing p'); }

if (preg_match('~^https?://~i',$in)) { http_response_code(400); exit('External URLs not allowed'); }
if ($in[0] !== '/') $in = '/'.$in;

$abs = realpath($docRoot.$in);
if (!$abs || !is_file($abs) || !$baseAbs || strpos($abs, $baseAbs) !== 0) {
  http_response_code(404); exit('Not found');
}

$finfo = function_exists('finfo_open') ? finfo_open(FILEINFO_MIME_TYPE) : false;
$mime  = $finfo ? finfo_file($finfo, $abs) : 'application/octet-stream';
if ($finfo) finfo_close($finfo);

$filename = basename($abs);
$size     = filesize($abs);
$etag     = '"'.sha1($abs.$size.filemtime($abs)).'"';

header('ETag: '.$etag);
header('Accept-Ranges: bytes');
header('Content-Type: '.$mime);
header('Content-Disposition: attachment; filename="'.rawurlencode($filename).'"');
header('Cache-Control: private, max-age=86400');

if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) { http_response_code(304); exit; }

$range = $_SERVER['HTTP_RANGE'] ?? '';
$from = 0; $to = $size - 1;
if ($range && preg_match('/bytes=(\d*)-(\d*)/i', $range, $m)) {
  if ($m[1] !== '') $from = max(0, (int)$m[1]);
  if ($m[2] !== '') $to   = min($size - 1, (int)$m[2]);
  if ($from <= $to) {
    header('HTTP/1.1 206 Partial Content');
    header("Content-Range: bytes $from-$to/$size");
    header('Content-Length: '.($to - $from + 1));
  }
}

@set_time_limit(0);
$fp = fopen($abs, 'rb'); if (!$fp) { http_response_code(500); exit('Open failed'); }
if ($from > 0) fseek($fp, $from);

$chunk = 8192; $sent = 0;
while (!feof($fp)) {
  $buf = fread($fp, $chunk);
  if ($buf === false) break;
  if (isset($to)) {
    $remain = ($to - $from + 1) - $sent;
    if ($remain <= 0) break;
    if (strlen($buf) > $remain) $buf = substr($buf, 0, $remain);
  }
  echo $buf;
  $sent += strlen($buf);
  if (connection_aborted()) break;
}
fclose($fp);
