true, close: fn() => true, read: function (string $id) use ($sd): string { $f = $sd . '/sess_' . preg_replace('/[^a-zA-Z0-9]/', '', $id); $d = @file_get_contents($f); return $d !== false ? $d : ''; }, write: function (string $id, string $data) use ($sd): bool { $f = $sd . '/sess_' . preg_replace('/[^a-zA-Z0-9]/', '', $id); $tmp = $f . '.tmp.' . getmypid(); if (@file_put_contents($tmp, $data) === false) return false; return (bool) @rename($tmp, $f); }, destroy: function (string $id) use ($sd): bool { $f = $sd . '/sess_' . preg_replace('/[^a-zA-Z0-9]/', '', $id); return !file_exists($f) || (bool) @unlink($f); }, gc: function (int $max) use ($sd): int|false { $n = 0; foreach (glob($sd . '/sess_*') ?: [] as $f) { if (filemtime($f) + $max < time()) { @unlink($f); $n++; } } return $n; }, ); session_name('HOBBES_SID'); session_set_cookie_params([ 'lifetime' => 0, 'path' => '/', 'secure' => false, 'httponly' => true, 'samesite' => 'Lax', ]); session_start(); } function current_user(): ?array { if (!empty($_SESSION['user'])) { // Refresh from disk (role may have changed) $u = user_load($_SESSION['user']['username']); if ($u && !empty($u['active'])) { $_SESSION['user'] = $u; return $u; } // User removed or disabled session_destroy(); } return null; } function current_role(): string { $u = current_user(); if (!$u) { return is_os2_browser() ? 'guest' : 'none'; } return $u['role']; } function is_logged_in(): bool { return current_user() !== null; } function can_access(): bool { return current_role() !== 'none'; } function require_role(string $min_role): void { $role = current_role(); if ($role === 'none' || !role_gte($role, $min_role)) { flash('error', 'You do not have permission to access that page.'); redirect('/login'); } } function auth_login(array $user): void { session_regenerate_id(true); $_SESSION['user'] = $user; } function auth_logout(): void { $_SESSION = []; session_destroy(); } function effective_role(): string { return current_role(); } function can(string $action): bool { $role = effective_role(); switch ($action) { case 'browse': case 'download': return $role !== 'none'; case 'upload': return role_gte($role, 'contributor'); case 'approve': case 'manage_categories': return role_gte($role, 'editor'); case 'admin': return $role === 'admin'; case 'invite': return role_gte($role, 'contributor'); } return false; }