0, 'path' => '/', 'domain' => '', 'secure' => $is_https, 'httponly' => true, 'samesite' => 'Lax', ]); session_start(); } function json_response(array $data, int $status_code = 200): void { http_response_code($status_code); header('Content-Type: application/json'); echo json_encode($data); exit; } function require_same_origin_request(): void { if (!in_array($_SERVER['REQUEST_METHOD'] ?? 'GET', ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { return; } $host = $_SERVER['HTTP_HOST'] ?? ''; $source = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_REFERER'] ?? ''; if ($source === '') { return; } $source_host = parse_url($source, PHP_URL_HOST); $source_port = parse_url($source, PHP_URL_PORT); if ($source_host && $source_port) { $source_host .= ':' . $source_port; } if (!$source_host || !hash_equals(strtolower($host), strtolower($source_host))) { json_response(['success' => false, 'message' => 'Invalid request origin.'], 403); } } function require_logged_in(): void { if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true || empty($_SESSION['id'])) { json_response(['success' => false, 'message' => 'Not logged in'], 401); } } function is_admin_session(): bool { return !empty($_SESSION['permissions']) && is_string($_SESSION['permissions']) && isset($_SESSION['permissions'][0]) && $_SESSION['permissions'][0] === '1'; } function remember_token_hash(string $token): string { return hash('sha256', $token); } function set_secure_cookie(string $name, string $value, int $expires): void { $is_https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443); setcookie($name, $value, [ 'expires' => $expires, 'path' => '/', 'secure' => $is_https, 'httponly' => true, 'samesite' => 'Lax', ]); } function delete_cookie(string $name): void { set_secure_cookie($name, '', time() - 3600); } function normalize_redirect_target(?string $target): string { $target = trim((string) $target); if ($target === '') { return '/account/'; } if (preg_match('/[\r\n]/', $target)) { return '/account/'; } if (str_starts_with($target, '/') && !str_starts_with($target, '//')) { return $target; } $parts = parse_url($target); if (!$parts || empty($parts['scheme']) || empty($parts['host'])) { return '/account/'; } if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) { return '/account/'; } return $target; } function append_auth_token_to_redirect(string $redirect, string $auth_token): string { $separator = str_contains($redirect, '?') ? '&' : '?'; return $redirect . $separator . 'auth=' . rawurlencode($auth_token); } ?>