| Server IP : 217.160.0.244 / Your IP : 216.73.216.153 Web Server : Apache System : Linux infong-eu155 4.4.400-icpu-108 #2 SMP Wed Feb 11 11:51:01 UTC 2026 x86_64 User : u100174116 ( 6746176) PHP Version : 8.5.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /homepages/13/d818981593/htdocs/GutTrechowNeu/hofladen/ |
Upload File : |
<?php
/**
* Gut Trechow – Backend v5
* ========================
* Benutzername und Passwort NUR hier ändern:
*/
$GT_USER = 'admin';
$GT_PASS = 'trechow2024';
// ── Pfade ──
$DATA = __DIR__ . '/data/';
$UPLOADS = __DIR__ . '/assets/uploads/admin/';
// ── CORS & JSON-Header ──
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET,POST,DELETE,PUT,OPTIONS');
header('Access-Control-Allow-Headers: Content-Type,X-GT-Session');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
// ── Hilfsfunktionen ──
function gt_ok($d = null): void {
echo json_encode(['ok' => true, 'data' => $d], JSON_UNESCAPED_UNICODE);
exit;
}
function gt_fail(string $msg, int $code = 400): void {
http_response_code($code);
echo json_encode(['ok' => false, 'error' => $msg], JSON_UNESCAPED_UNICODE);
exit;
}
function gt_body(): array {
$raw = @file_get_contents('php://input');
if (!$raw) return [];
$d = json_decode($raw, true);
return is_array($d) ? $d : [];
}
function gt_clean(string $s, int $max = 1000): string {
$s = strip_tags(trim($s));
return function_exists('mb_substr') ? mb_substr($s, 0, $max) : substr($s, 0, $max);
}
function gt_len(string $s): int {
return function_exists('mb_strlen') ? mb_strlen($s) : strlen($s);
}
// ── Data-Ordner initialisieren ──
function gt_data_dir(): bool {
global $DATA;
if (is_dir($DATA)) return true;
if (!@mkdir($DATA, 0750, true)) return false;
@file_put_contents($DATA . '.htaccess', "Deny from all\nOptions -Indexes\n");
@file_put_contents($DATA . 'index.php', '<?php // silence');
return true;
}
function gt_read(string $file, $default) {
global $DATA;
$path = $DATA . $file;
if (!file_exists($path)) return $default;
$d = @json_decode(@file_get_contents($path), true);
return is_array($d) ? $d : $default;
}
function gt_write(string $file, $data): bool {
global $DATA;
if (!gt_data_dir()) return false;
return @file_put_contents($DATA . $file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX) !== false;
}
// ── Credentials ──
function gt_get_creds(): array {
global $GT_USER, $GT_PASS;
$c = gt_read('credentials.json', []);
return [
'user' => (isset($c['user']) && $c['user'] !== '') ? $c['user'] : $GT_USER,
'pass' => (isset($c['pass']) && $c['pass'] !== '') ? $c['pass'] : $GT_PASS,
];
}
// ── Sessions (HMAC-basiert, braucht keine Datei) ──
// Session-Token = base64(json) + "." + HMAC
// So funktioniert Login auch wenn data/ nicht beschreibbar
define('GT_SECRET', hash('sha256', __FILE__ . 'gt_secret_2024_' . php_uname('n')));
define('GT_TTL', 28800); // 8 Stunden
function gt_sess_create(string $user): string {
$payload = ['u' => $user, 'e' => time() + GT_TTL];
$b64 = rtrim(base64_encode(json_encode($payload)), '=');
$sig = hash_hmac('sha256', $b64, GT_SECRET);
return $b64 . '.' . $sig;
}
function gt_sess_verify(string $token): ?string {
$parts = explode('.', $token, 2);
if (count($parts) !== 2) return null;
[$b64, $sig] = $parts;
$expected = hash_hmac('sha256', $b64, GT_SECRET);
if (!hash_equals($expected, $sig)) return null;
$payload = json_decode(base64_decode($b64 . '=='), true);
if (!is_array($payload) || ($payload['e'] ?? 0) < time()) return null;
return $payload['u'] ?? null;
}
function gt_sess_from_request(): ?string {
$token = $_SERVER['HTTP_X_GT_SESSION'] ?? '';
if (strlen($token) < 10) return null;
return gt_sess_verify($token);
}
function gt_require_auth(): void {
if (!gt_sess_from_request()) gt_fail('Nicht authentifiziert.', 401);
}
function gt_slug_to_file(string $slug): ?string {
$slug = trim($slug, '/');
if ($slug === '' || $slug === 'index') return __DIR__ . '/index.html';
if (!preg_match('~^[a-z0-9\-/]+$~i', $slug)) return null;
$path = __DIR__ . '/' . $slug . '/index.html';
return file_exists($path) ? $path : null;
}
function gt_page_path_prefix(string $slug): string {
$slug = trim($slug, '/');
if ($slug === '' || $slug === 'index') return '';
return str_repeat('../', substr_count($slug, '/') + 1);
}
function gt_path_to_builder(string $path, string $slug): string {
$path = trim((string)$path);
if ($path === '' || preg_match('~^(https?:)?//|^data:|^#~i', $path)) return $path;
$path = preg_replace('~^(?:\./)+~', '', $path);
$path = preg_replace('~^(?:\.\./)+~', '', $path);
return ltrim($path, '/');
}
function gt_path_from_builder(string $path, string $slug): string {
$path = trim((string)$path);
if ($path === '' || preg_match('~^(https?:)?//|^data:|^#~i', $path)) return $path;
if (preg_match('~^(?:\.\./)+~', $path)) return $path;
if (str_starts_with($path, '/')) return $path;
return gt_page_path_prefix($slug) . ltrim($path, '/');
}
function gt_page_extract(string $html, string $slug): array {
$title = '';
$desc = '';
$content = '';
$hero = ['img' => '', 'kicker' => '', 'heading' => ''];
if (preg_match('~<title>(.*?)</title>~is', $html, $m)) $title = html_entity_decode(trim(strip_tags($m[1])), ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (preg_match('~<meta\s+name=["\']description["\']\s+content=["\'](.*?)["\']~is', $html, $m)) $desc = html_entity_decode(trim($m[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (preg_match('~<article\b[^>]*class=["\'][^"\']*page-content[^"\']*["\'][^>]*>(.*?)</article>~is', $html, $m)) $content = trim($m[1]);
if (preg_match('~<section\b[^>]*class=["\'][^"\']*hero-banner[^"\']*["\'][^>]*>(.*?)</section>~is', $html, $m)) {
$heroHtml = $m[1];
if (preg_match('~<img\b[^>]*src=["\']([^"\']+)["\']~is', $heroHtml, $mm)) $hero['img'] = gt_path_to_builder($mm[1], $slug);
if (preg_match('~<p\b[^>]*class=["\'][^"\']*hero-kicker[^"\']*["\'][^>]*>(.*?)</p>~is', $heroHtml, $mm)) $hero['kicker'] = html_entity_decode(trim(strip_tags($mm[1])), ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (preg_match('~<h1\b[^>]*>(.*?)</h1>~is', $heroHtml, $mm)) $hero['heading'] = html_entity_decode(trim(strip_tags($mm[1])), ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
return [
'title' => $title,
'content' => $content,
'meta' => ['title' => $title, 'desc' => $desc],
'hero' => $hero,
];
}
function gt_page_read_live(string $slug): ?array {
$file = gt_slug_to_file($slug);
if (!$file) return null;
$html = @file_get_contents($file);
if ($html === false) return null;
return gt_page_extract($html, $slug);
}
function gt_page_write_live(string $slug, array $page): bool {
$file = gt_slug_to_file($slug);
if (!$file) return false;
$html = @file_get_contents($file);
if ($html === false) return false;
$title = $page['meta']['title'] ?? $page['title'] ?? '';
$desc = $page['meta']['desc'] ?? '';
$content = (string)($page['content'] ?? '');
$hero = is_array($page['hero'] ?? null) ? $page['hero'] : [];
if ($title !== '') {
$safeTitle = htmlspecialchars($title, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$html = preg_replace('~<title>.*?</title>~is', '<title>' . $safeTitle . '</title>', $html, 1) ?? $html;
}
$safeDesc = htmlspecialchars($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8');
if (preg_match('~<meta\s+name=["\']description["\']\s+content=["\'].*?["\']~is', $html)) {
$html = preg_replace('~<meta\s+name=["\']description["\']\s+content=["\'].*?["\']~is', '<meta name="description" content="' . $safeDesc . '"', $html, 1) ?? $html;
}
if ($content !== '') {
$html = preg_replace('~(<article\b[^>]*class=["\'][^"\']*page-content[^"\']*["\'][^>]*>)(.*?)(</article>)~is', '$1' . "
" . $content . "
" . '$3', $html, 1) ?? $html;
}
if ($hero) {
$heroImg = isset($hero['img']) ? gt_path_from_builder((string)$hero['img'], $slug) : null;
$heroKicker = isset($hero['kicker']) ? htmlspecialchars((string)$hero['kicker'], ENT_QUOTES | ENT_HTML5, 'UTF-8') : null;
$heroHeading = isset($hero['heading']) ? htmlspecialchars((string)$hero['heading'], ENT_QUOTES | ENT_HTML5, 'UTF-8') : null;
$html = preg_replace_callback('~<section\b([^>]*)class=["\']([^"\']*hero-banner[^"\']*)["\']([^>]*)>(.*?)</section>~is', function($m) use ($heroImg, $heroKicker, $heroHeading) {
$section = $m[0];
if ($heroImg !== null && $heroImg !== '') $section = preg_replace('~(<img\b[^>]*src=["\'])[^"\']+(["\'])~is', '$1' . $heroImg . '$2', $section, 1) ?? $section;
if ($heroKicker !== null) $section = preg_replace('~(<p\b[^>]*class=["\'][^"\']*hero-kicker[^"\']*["\'][^>]*>)(.*?)(</p>)~is', '$1' . $heroKicker . '$3', $section, 1) ?? $section;
if ($heroHeading !== null && $heroHeading !== '') $section = preg_replace('~(<h1\b[^>]*>)(.*?)(</h1>)~is', '$1' . $heroHeading . '$3', $section, 1) ?? $section;
return $section;
}, $html, 1) ?? $html;
}
return @file_put_contents($file, $html, LOCK_EX) !== false;
}
// ── Routing ──
$act = $_GET['action'] ?? '';
$method = $_SERVER['REQUEST_METHOD'];
// ════════ AUTH ════════
if ($act === 'login' && $method === 'POST') {
$b = gt_body();
$u = trim($b['user'] ?? '');
$p = $b['pass'] ?? '';
$c = gt_get_creds();
// Vergleich — kein hash_equals, kein bcrypt, direkter Vergleich
if ($u === '' || $p === '') {
gt_fail('Bitte Benutzername und Passwort eingeben.');
}
if ($u !== $c['user'] || $p !== $c['pass']) {
sleep(1); // Brute-Force verlangsamen
gt_fail('Benutzername oder Passwort falsch.', 401);
}
gt_ok(['token' => gt_sess_create($u), 'user' => $u]);
}
if ($act === 'logout' && $method === 'POST') {
// Stateless sessions brauchen kein serverseitiges Löschen
gt_ok();
}
if ($act === 'check' && $method === 'GET') {
$u = gt_sess_from_request();
gt_ok(['ok' => (bool)$u, 'user' => $u]);
}
// ════════ PASSWORT ÄNDERN ════════
if ($act === 'change_password' && $method === 'POST') {
gt_require_auth();
$b = gt_body();
$np = trim($b['new_pass'] ?? '');
if (strlen($np) < 6) gt_fail('Passwort muss mindestens 6 Zeichen haben.');
$c = gt_get_creds();
gt_write('credentials.json', ['user' => $c['user'], 'pass' => $np]);
gt_ok();
}
// ════════ WARTUNGSMODUS ════════
if ($act === 'maintenance' && $method === 'GET') {
$d = gt_read('maintenance.json', ['active' => false]);
gt_ok(['active' => (bool)($d['active'] ?? false)]);
}
if ($act === 'maintenance' && $method === 'POST') {
gt_require_auth();
$active = (bool)(gt_body()['active'] ?? false);
gt_write('maintenance.json', ['active' => $active]);
gt_ok(['active' => $active]);
}
// ════════ GÄSTEBUCH ════════
if ($act === 'guestbook' && $method === 'GET') {
$d = gt_read('guestbook.json', ['entries' => []]);
$pub = array_values(array_filter($d['entries'] ?? [], fn($e) => !empty($e['approved'])));
usort($pub, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
gt_ok($pub);
}
if ($act === 'guestbook' && $method === 'POST') {
$b = gt_body();
$name = gt_clean($b['name'] ?? '', 80);
$context = gt_clean($b['context'] ?? '', 100);
$message = gt_clean($b['message'] ?? '', 800);
if (!$name || gt_len($name) < 2) gt_fail('Name zu kurz.');
if (!$message || gt_len($message) < 5) gt_fail('Nachricht zu kurz.');
$entry = ['id' => 'gb-' . time() . '-' . bin2hex(random_bytes(3)), 'name' => $name,
'context' => $context, 'message' => $message, 'date' => date('c'), 'approved' => false];
$d = gt_read('guestbook.json', ['entries' => []]);
$d['entries'][] = $entry;
gt_write('guestbook.json', $d);
gt_ok(['id' => $entry['id']]);
}
if ($act === 'guestbook_admin' && $method === 'GET') {
gt_require_auth();
$d = gt_read('guestbook.json', ['entries' => []]);
$pnd = array_values(array_filter($d['entries'] ?? [], fn($e) => empty($e['approved'])));
$apr = array_values(array_filter($d['entries'] ?? [], fn($e) => !empty($e['approved'])));
usort($pnd, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
usort($apr, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
gt_ok(['pending' => $pnd, 'approved' => $apr]);
}
if ($act === 'approve' && $method === 'POST') {
gt_require_auth();
$id = gt_clean($_GET['id'] ?? '', 60);
$d = gt_read('guestbook.json', ['entries' => []]);
$found = false;
foreach ($d['entries'] as &$e) { if ($e['id'] === $id) { $e['approved'] = true; $found = true; break; } }
unset($e);
if (!$found) gt_fail('Nicht gefunden.', 404);
gt_write('guestbook.json', $d);
gt_ok();
}
if ($act === 'delete_entry' && $method === 'DELETE') {
gt_require_auth();
$id = gt_clean($_GET['id'] ?? '', 60);
$d = gt_read('guestbook.json', ['entries' => []]);
$d['entries'] = array_values(array_filter($d['entries'], fn($e) => $e['id'] !== $id));
gt_write('guestbook.json', $d);
gt_ok();
}
// ════════ SEITEN ════════
if ($act === 'get_page' && $method === 'GET') {
$slug = trim((string)($_GET['slug'] ?? ''), '/');
if ($slug === '') $slug = 'index';
$pages = gt_read('pages.json', []);
$live = gt_page_read_live($slug);
$stored = $pages[$slug] ?? null;
if (is_array($stored)) {
if ($live) {
$merged = $live;
foreach (['title','content','updated'] as $k) if (isset($stored[$k])) $merged[$k] = $stored[$k];
if (isset($stored['meta']) && is_array($stored['meta'])) $merged['meta'] = array_merge($merged['meta'] ?? [], $stored['meta']);
if (isset($stored['hero']) && is_array($stored['hero'])) $merged['hero'] = array_merge($merged['hero'] ?? [], $stored['hero']);
if (isset($stored['blocks'])) $merged['blocks'] = $stored['blocks'];
gt_ok($merged);
}
gt_ok($stored);
}
gt_ok($live);
}
if ($act === 'save_page' && $method === 'POST') {
gt_require_auth();
$b = gt_body();
$slug = trim((string)($b['slug'] ?? ''), '/');
if ($slug === '') gt_fail('Kein Slug.');
$pages = gt_read('pages.json', []);
$page = [
'title' => gt_clean($b['title'] ?? '', 200),
'content' => (string)($b['content'] ?? ''),
'updated' => date('c'),
'meta' => is_array($b['meta'] ?? null) ? [
'title' => gt_clean($b['meta']['title'] ?? ($b['title'] ?? ''), 200),
'desc' => gt_clean($b['meta']['desc'] ?? '', 500),
] : ['title' => gt_clean($b['title'] ?? '', 200), 'desc' => ''],
'hero' => is_array($b['hero'] ?? null) ? [
'img' => gt_path_to_builder((string)($b['hero']['img'] ?? ''), $slug),
'kicker' => gt_clean($b['hero']['kicker'] ?? '', 120),
'heading' => gt_clean($b['hero']['heading'] ?? ($b['title'] ?? ''), 200),
] : ['img' => '', 'kicker' => '', 'heading' => gt_clean($b['title'] ?? '', 200)],
'blocks' => is_array($b['blocks'] ?? null) ? $b['blocks'] : [],
];
$pages[$slug] = $page;
if (!gt_write('pages.json', $pages)) gt_fail('Seite konnte nicht gespeichert werden.', 500);
if (!gt_page_write_live($slug, $page)) gt_fail('HTML-Datei konnte nicht aktualisiert werden.', 500);
gt_ok($page);
}
if ($act === 'get_all_pages' && $method === 'GET') {
gt_require_auth();
gt_ok(gt_read('pages.json', []));
}
// ════════ VERANSTALTUNGEN ════════
if ($act === 'get_events' && $method === 'GET') {
$d = gt_read('events.json', ['events' => []]);
$evs = $d['events'] ?? [];
usort($evs, fn($a, $b) => strcmp($a['date'] ?? '', $b['date'] ?? ''));
gt_ok($evs);
}
if ($act === 'save_event' && $method === 'POST') {
gt_require_auth();
$b = gt_body();
$d = gt_read('events.json', ['events' => []]);
$id = gt_clean($b['id'] ?? '', 60);
$ev = [
'id' => $id ?: ('ev-' . time() . '-' . bin2hex(random_bytes(3))),
'title' => gt_clean($b['title'] ?? '', 200),
'date' => gt_clean($b['date'] ?? '', 20),
'time' => gt_clean($b['time'] ?? '', 10),
'location' => gt_clean($b['location'] ?? '', 200),
'description' => gt_clean($b['description'] ?? '', 2000),
'updated' => date('c'),
];
if ($id) {
$found = false;
foreach ($d['events'] as &$e) { if ($e['id'] === $id) { $e = $ev; $found = true; break; } }
unset($e);
if (!$found) $d['events'][] = $ev;
} else {
$d['events'][] = $ev;
}
gt_write('events.json', $d);
gt_ok($ev);
}
if ($act === 'delete_event' && $method === 'DELETE') {
gt_require_auth();
$id = gt_clean($_GET['id'] ?? '', 60);
$d = gt_read('events.json', ['events' => []]);
$d['events'] = array_values(array_filter($d['events'], fn($e) => $e['id'] !== $id));
gt_write('events.json', $d);
gt_ok();
}
// ════════ DATEI-UPLOAD ════════
if ($act === 'upload' && $method === 'POST') {
gt_require_auth();
if (empty($_FILES['file'])) gt_fail('Keine Datei.');
$f = $_FILES['file'];
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf'];
$ext = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) gt_fail('Dateityp nicht erlaubt.');
if ($f['size'] > 10 * 1024 * 1024) gt_fail('Max. 10 MB.');
if (!is_dir($UPLOADS)) @mkdir($UPLOADS, 0755, true);
$fname = time() . '-' . bin2hex(random_bytes(4)) . '.' . $ext;
if (!@move_uploaded_file($f['tmp_name'], $UPLOADS . $fname)) gt_fail('Upload fehlgeschlagen.');
gt_ok(['url' => 'assets/uploads/admin/' . $fname, 'name' => $f['name']]);
}
if ($act === 'list_uploads' && $method === 'GET') {
gt_require_auth();
if (!is_dir($UPLOADS)) { gt_ok([]); return; }
$files = [];
foreach (glob($UPLOADS . '*') as $fp) {
$n = basename($fp);
if (in_array($n, ['.', '..', '.htaccess'])) continue;
$files[] = ['name' => $n, 'url' => 'assets/uploads/admin/' . $n, 'size' => @filesize($fp), 'time' => @filemtime($fp)];
}
usort($files, fn($a, $b) => $b['time'] - $a['time']);
gt_ok($files);
}
if ($act === 'delete_upload' && $method === 'DELETE') {
gt_require_auth();
$name = basename($_GET['name'] ?? '');
if (!$name || !file_exists($UPLOADS . $name)) gt_fail('Nicht gefunden.', 404);
@unlink($UPLOADS . $name);
gt_ok();
}
gt_fail('Unbekannte Aktion.', 404);