&e= * Das Plugin muss die Berechtigung "api.public" anfordern und prüft die Echtheit jeder Anfrage selbst (siehe plugin_register_webhook()). * Hier gibt es nur: Methode POST, höchstens 256 KB, Aufruf des Handlers, Antwort als JSON oder Text. */ require_once __DIR__ . '/../../../includes/bootstrap.php'; header('Cache-Control: no-store'); header('X-Content-Type-Options: nosniff'); function webhook_fail(int $status, string $message): never { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); echo json_encode(['error' => $message], JSON_UNESCAPED_UNICODE); exit; } if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { header('Allow: POST'); webhook_fail(405, 'Nur POST.'); } $slug = (string) ($_GET['p'] ?? ''); $endpoint = (string) ($_GET['e'] ?? ''); $handler = plugin_state()['webhooks'][$slug][$endpoint] ?? null; if (!is_callable($handler)) webhook_fail(404, 'Unbekannte Adresse.'); $body = (string) file_get_contents('php://input', false, null, 0, 262145); if (strlen($body) > 262144) webhook_fail(413, 'Anfrage zu groß.'); $headers = []; foreach ($_SERVER as $k => $v) { if (str_starts_with($k, 'HTTP_')) $headers[strtolower(str_replace('_', '-', substr($k, 5)))] = (string) $v; } if (isset($_SERVER['CONTENT_TYPE'])) $headers['content-type'] = (string) $_SERVER['CONTENT_TYPE']; $result = plugin_guard($slug, fn() => $handler(['method' => 'POST', 'headers' => $headers, 'body' => $body, 'query' => $_GET, 'ip' => client_ip()]), null); if (!is_array($result)) webhook_fail(500, 'Fehler im Plugin.'); http_response_code((int) ($result['status'] ?? 200)); foreach ((array) ($result['headers'] ?? []) as $name => $value) { if (preg_match('/^[A-Za-z0-9-]+$/', (string) $name) && !preg_match('/[\r\n]/', (string) $value)) header($name . ': ' . $value); } if (isset($result['json'])) { header('Content-Type: application/json; charset=utf-8'); echo json_encode($result['json'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } else { echo (string) ($result['body'] ?? ''); }