You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.9 KiB
215 lines
6.9 KiB
<?php
|
|
session_start();
|
|
|
|
define('PASSWORD', 'grund_cttue_gesetz');
|
|
define('FILES_DIR', __DIR__ . '/avj2305');
|
|
define('ACTIVE_FILE', __DIR__ . '/active.txt');
|
|
|
|
// =============================================================================
|
|
// HELPERS
|
|
// =============================================================================
|
|
|
|
function redirect(string $url): never {
|
|
header('Location: ' . $url);
|
|
exit;
|
|
}
|
|
|
|
function require_auth(): void {
|
|
if (empty($_SESSION['auth'])) redirect('?login');
|
|
}
|
|
|
|
function get_active(): string {
|
|
if (!file_exists(ACTIVE_FILE)) return '';
|
|
$v = trim(file_get_contents(ACTIVE_FILE));
|
|
return $v !== '' ? $v : '';
|
|
}
|
|
|
|
function set_active(string $filename): void {
|
|
file_put_contents(ACTIVE_FILE, $filename);
|
|
}
|
|
|
|
function get_bin_files(): array {
|
|
$files = glob(FILES_DIR . '/*.bin');
|
|
return $files ? array_map('basename', $files) : [];
|
|
}
|
|
|
|
function safe_filename(string $name): bool {
|
|
return $name === basename($name)
|
|
&& str_ends_with($name, '.bin')
|
|
&& !str_contains($name, "\0");
|
|
}
|
|
|
|
function has_png(string $bin_basename): bool {
|
|
$png = substr($bin_basename, 0, -4) . '.png';
|
|
return file_exists(FILES_DIR . '/' . $png);
|
|
}
|
|
|
|
function png_path(string $bin_basename): string {
|
|
return FILES_DIR . '/' . substr($bin_basename, 0, -4) . '.png';
|
|
}
|
|
|
|
// =============================================================================
|
|
// ACTIONS (POST handlers - redirect, never render)
|
|
// =============================================================================
|
|
|
|
function action_login(): never {
|
|
if (hash_equals(PASSWORD, $_POST['password'] ?? '')) {
|
|
$_SESSION['auth'] = true;
|
|
redirect('?edit');
|
|
}
|
|
redirect('?login&err=1');
|
|
}
|
|
|
|
function action_set_file(): never {
|
|
require_auth();
|
|
$f = $_POST['file'] ?? '';
|
|
if (safe_filename($f) && file_exists(FILES_DIR . '/' . $f)) {
|
|
set_active($f);
|
|
}
|
|
redirect('?edit');
|
|
}
|
|
|
|
// =============================================================================
|
|
// RENDERERS (GET handlers - output HTML, never redirect)
|
|
// =============================================================================
|
|
|
|
function render_login(): never {
|
|
$err = !empty($_GET['err']); ?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><title>Login</title>
|
|
<style>
|
|
body{font:14px monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#111;color:#eee}
|
|
form{display:flex;flex-direction:column;gap:8px;width:220px}
|
|
input[type=password]{padding:6px;background:#222;border:1px solid #555;color:#eee}
|
|
button{padding:6px;background:#444;border:none;color:#eee;cursor:pointer}
|
|
button:hover{background:#555}
|
|
.err{color:#f66;font-size:12px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<form method="post" action="?login">
|
|
<label>Password</label>
|
|
<input type="password" name="password" autofocus>
|
|
<button type="submit">Login</button>
|
|
<?php if ($err): ?><span class="err">Wrong password.</span><?php endif; ?>
|
|
</form>
|
|
</body></html>
|
|
<?php exit; }
|
|
|
|
function render_edit(): never {
|
|
require_auth();
|
|
$files = get_bin_files();
|
|
$active = get_active(); ?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><title>Select File</title>
|
|
<style>
|
|
body{font:14px monospace;background:#111;color:#eee;padding:24px;margin:0}
|
|
h2{margin:0 0 16px}
|
|
ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px}
|
|
li form{margin:0}
|
|
.row{display:flex;align-items:center;gap:10px}
|
|
button{padding:6px 12px;background:#333;border:1px solid #555;color:#eee;cursor:pointer;flex:1;text-align:left}
|
|
button:hover{background:#444}
|
|
button.active{border-color:#6af;color:#6af}
|
|
.thumb{width:480px;height:270px;object-fit:cover;border:1px solid #444;background:#222;flex-shrink:0}
|
|
.nothumb{width:64px;height:64px;flex-shrink:0}
|
|
.none{color:#888}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h2>Live file selector</h2>
|
|
<p>Active: <strong><?= $active !== '' ? htmlspecialchars($active) : '<span class="none">none</span>' ?></strong></p>
|
|
<?php if ($files): ?>
|
|
<ul>
|
|
<?php foreach ($files as $f): ?>
|
|
<li>
|
|
<form method="post" action="?edit">
|
|
<div class="row">
|
|
<?php if (has_png($f)): ?>
|
|
<img class="thumb" src="?img=<?= urlencode($f) ?>" alt="">
|
|
<?php else: ?>
|
|
<div class="nothumb"></div>
|
|
<?php endif; ?>
|
|
<input type="hidden" name="file" value="<?= htmlspecialchars($f) ?>">
|
|
<button type="submit"<?= $f === $active ? ' class="active"' : '' ?>><?= htmlspecialchars($f) ?></button>
|
|
</div>
|
|
</form>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php else: ?>
|
|
<p class="none">No .bin files found in avj2305/</p>
|
|
<?php endif; ?>
|
|
</body></html>
|
|
<?php exit; }
|
|
|
|
// =============================================================================
|
|
// FILE SERVER (default route)
|
|
// =============================================================================
|
|
|
|
function serve_png(): never {
|
|
require_auth();
|
|
$f = $_GET['img'] ?? '';
|
|
if (!safe_filename($f)) {
|
|
http_response_code(400); exit('Invalid filename.');
|
|
}
|
|
$path = png_path($f);
|
|
if (!file_exists($path)) {
|
|
http_response_code(404); exit('No preview.');
|
|
}
|
|
header('Content-Type: image/png');
|
|
header('Content-Length: ' . filesize($path));
|
|
readfile($path);
|
|
exit;
|
|
}
|
|
|
|
function serve_active_file(): never {
|
|
$active = get_active();
|
|
|
|
if ($active === '') {
|
|
http_response_code(404); exit('No active file set.');
|
|
}
|
|
if (!safe_filename($active)) {
|
|
http_response_code(500); exit('Invalid filename.');
|
|
}
|
|
|
|
$path = FILES_DIR . '/' . $active;
|
|
|
|
if (!file_exists($path)) {
|
|
http_response_code(404); exit('File not found.');
|
|
}
|
|
|
|
header('Content-Type: application/octet-stream');
|
|
header('Content-Disposition: inline; filename="' . addslashes($active) . '"');
|
|
header('Content-Length: ' . filesize($path));
|
|
readfile($path);
|
|
exit;
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// ROUTES
|
|
// =============================================================================
|
|
//
|
|
// GET /live.php -> serve active .bin file
|
|
// GET /live.php?login -> login page
|
|
// POST /live.php?login -> process login
|
|
// GET /live.php?edit -> file picker [auth required]
|
|
// POST /live.php?edit -> set active file [auth required]
|
|
// GET /live.php?img=x.bin -> serve x.png preview [auth required]
|
|
//
|
|
// =============================================================================
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$route = array_key_first($_GET) ?? '';
|
|
|
|
match (true) {
|
|
$method === 'POST' && $route === 'login' => action_login(),
|
|
$method === 'POST' && $route === 'edit' => action_set_file(),
|
|
$method === 'GET' && $route === 'login' => render_login(),
|
|
$method === 'GET' && $route === 'edit' => render_edit(),
|
|
$method === 'GET' && $route === 'img' => serve_png(),
|
|
default => serve_active_file(),
|
|
};
|
|
|