<?php
session_start();

function base64url_decode($data) {
    $remainder = strlen($data) % 4;
    if ($remainder) {
        $data .= str_repeat('=', 4 - $remainder);
    }
    return base64_decode(strtr($data, '-_', '+/'));
}

function verify_token($token, $secret) {
    $parts = explode('.', $token);
    if (count($parts) !== 2) return false;

    [$b64, $sig] = $parts;
    $calc = rtrim(strtr(base64_encode(hash_hmac('sha256', $b64, $secret, true)), '+/', '-_'), '=');

    if (!hash_equals($calc, $sig)) return false;

    $payload = json_decode(base64url_decode($b64), true);
    if (!$payload) return false;

    // expiry
    if (time() > ($payload['exp'] ?? 0)) return false;

    // session binding
    if (($payload['sid'] ?? '') !== session_id()) return false;

    return $payload;
}

$secret = 'replace-with-strong-secret';

// basic rate limit (per IP, per minute)
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$rateFile = sys_get_temp_dir().'/dl_'.md5($ip);
$hits = is_file($rateFile) ? (int)file_get_contents($rateFile) : 0;
if ($hits > 20) {
    http_response_code(429);
    exit('Too many requests');
}
file_put_contents($rateFile, $hits + 1);

// verify token
$token = $_GET['token'] ?? '';
$payload = verify_token($token, $secret);
if (!$payload) {
    http_response_code(403);
    exit('Invalid or expired link');
}

// one-time use (simple file flag)
$usedKey = sys_get_temp_dir().'/used_'.sha1($token);
if (file_exists($usedKey)) {
    http_response_code(410);
    exit('Link already used');
}
file_put_contents($usedKey, '1');

// resolve file safely
$filename = basename($payload['file']); // prevent path traversal
$path = __DIR__ . '/downloads/' . $filename;

if (!is_file($path)) {
    http_response_code(404);
    exit('File not found');
}

// log download
$logLine = sprintf("[%s] %s %s\n", date('c'), $ip, $filename);
file_put_contents(__DIR__.'/download.log', $logLine, FILE_APPEND);

// send file
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Length: '.filesize($path));
readfile($path);
exit;
?>