' . ($isError ? '' : '') . ' ' . htmlspecialchars($statusTitle) . '

' . $messageHtml . '

Silias
Ihr Partner für moderne IT-Infrastruktur & Automatisierung
Silias KLG
IT, Automatisierung & individuelle Web-Lösungen
Wir bauen schlanke Tools wie dieses, automatisieren Prozesse und integrieren Systeme sauber in bestehende Umgebungen (Hosting, Mail, ERP/CRM, Schnittstellen).

Als Managed Service Provider betreuen wir KMU ganzheitlich – von der Infrastruktur über Cloud, Netzwerke und Telefonie bis zum laufenden Betrieb, Monitoring und persönlichen Support.
'; } /* ---------------- REQUEST AND ABUSE PROTECTION ---------------- */ if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { header('Allow: POST'); renderPage('Methode nicht erlaubt', 'Bitte starten Sie den Export über das Formular auf der Startseite.', true, 405); exit; } // Bots commonly fill fields that are deliberately hidden from real visitors. $honeypot = isset($_POST['website']) && is_scalar($_POST['website']) ? trim((string)$_POST['website']) : ''; if ($honeypot !== '') { renderPage( 'Auftrag eingegangen', 'Wir haben Ihren Auftrag erhalten. Sie erhalten von uns eine E-Mail, sobald die Daten zum Download bereit sind.', false ); exit; } if (!app_turnstile_is_configured()) { renderPage('Export nicht verfügbar', 'Der Missbrauchsschutz ist noch nicht vollständig konfiguriert.', true, 503); exit; } $clientIp = app_client_ip(); try { $ipLimit = app_rate_limit( 'submit-ip', $clientIp, app_env_int('RATE_LIMIT_IP_MAX', 3, 1, 100), app_env_int('RATE_LIMIT_IP_WINDOW_SECONDS', 900, 60, 86400) ); } catch (Throwable $exception) { error_log('Rate limit failure: ' . $exception->getMessage()); renderPage('Export nicht verfügbar', 'Der Export kann momentan nicht sicher verarbeitet werden. Bitte versuchen Sie es später erneut.', true, 503); exit; } if (!$ipLimit['allowed']) { header('Retry-After: ' . $ipLimit['retry_after']); renderPage('Zu viele Anfragen', 'Von dieser Verbindung wurden zu viele Aufträge gesendet. Bitte versuchen Sie es später erneut.', true, 429); exit; } $turnstileToken = isset($_POST['cf-turnstile-response']) && is_scalar($_POST['cf-turnstile-response']) ? (string)$_POST['cf-turnstile-response'] : ''; $turnstileResult = app_verify_turnstile($turnstileToken, $clientIp); if (!$turnstileResult['success']) { renderPage('Bot-Prüfung fehlgeschlagen', 'Die Sicherheitsprüfung konnte nicht bestätigt werden. Bitte laden Sie die Startseite neu und versuchen Sie es erneut.', true, 403); exit; } $formStarted = isset($_POST['form_started']) && is_scalar($_POST['form_started']) ? (string)$_POST['form_started'] : ''; if (!app_validate_form_started_token($formStarted)) { renderPage('Überprüfung fehlgeschlagen', 'Das Formular ist abgelaufen oder wurde zu schnell übermittelt. Bitte laden Sie die Startseite neu.', true, 400); exit; } /* ---------------- INPUT VALIDATION ---------------- */ $emailValue = $_POST['email'] ?? ''; $email = is_scalar($emailValue) ? strtolower(trim((string)$emailValue)) : ''; if ($email === '' || strlen($email) > 254 || !filter_var($email, FILTER_VALIDATE_EMAIL)) { renderPage('Ungültige E-Mail', 'Bitte geben Sie eine gültige E-Mail-Adresse an.', true, 400); exit; } $companyValue = $_POST['firma'] ?? ''; $companyName = is_scalar($companyValue) ? trim((string)$companyValue) : ''; $companyNameLength = function_exists('mb_strlen') ? mb_strlen($companyName) : strlen($companyName); if ($companyNameLength > 200 || preg_match('//u', $companyName) !== 1 || preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', $companyName)) { renderPage('Ungültiger Firmenname', 'Der Firmenname enthält ungültige Zeichen oder ist zu lang.', true, 400); exit; } $maximumSeats = app_env_int('MAX_SEATS_PER_JOB', 500, 1, 5000); $maximumLegalForms = app_env_int('MAX_LEGAL_FORMS_PER_JOB', 50, 1, 200); $seats = app_normalize_positive_id_list($_POST['sitze'] ?? null, $maximumSeats); $legalForms = app_normalize_positive_id_list($_POST['rechtsformen'] ?? null, $maximumLegalForms); if ($seats === null || $legalForms === null) { renderPage('Ungültige Auswahl', 'Bitte wählen Sie eine zulässige Anzahl Orte und Rechtsformen aus.', true, 400); exit; } sort($seats, SORT_NUMERIC); sort($legalForms, SORT_NUMERIC); $requestCount = count($seats) * count($legalForms); $maximumRequests = app_env_int('MAX_REQUESTS_PER_JOB', 5000, 1, 50000); if ($requestCount > $maximumRequests) { renderPage( 'Auswahl zu gross', 'Diese Auswahl würde ' . number_format($requestCount, 0, ',', "'") . ' einzelne Abfragen erzeugen. Erlaubt sind maximal ' . number_format($maximumRequests, 0, ',', "'") . '. Bitte schränken Sie Orte oder Rechtsformen ein.', true, 400 ); exit; } try { $emailLimit = app_rate_limit( 'submit-email', $email, app_env_int('RATE_LIMIT_EMAIL_MAX', 5, 1, 100), app_env_int('RATE_LIMIT_EMAIL_WINDOW_SECONDS', 86400, 300, 604800) ); } catch (Throwable $exception) { error_log('Rate limit failure: ' . $exception->getMessage()); renderPage('Export nicht verfügbar', 'Der Export kann momentan nicht sicher verarbeitet werden. Bitte versuchen Sie es später erneut.', true, 503); exit; } if (!$emailLimit['allowed']) { header('Retry-After: ' . $emailLimit['retry_after']); renderPage('Zu viele Anfragen', 'Für diese E-Mail-Adresse wurden heute bereits mehrere Exporte angefordert.', true, 429); exit; } /* ---------------- TASK BUILD AND ATOMIC QUEUE WRITE ---------------- */ $baseRequest = [ 'maxEntries' => 50, 'offset' => 0, 'languageKey' => 'de', 'deletedFirms' => isset($_POST['geloeschteRechtseinheiten']), 'searchType' => 'exact', ]; if ($companyName !== '') { $baseRequest['name'] = $companyName; } $requestsToDo = []; foreach ($legalForms as $legalForm) { foreach ($seats as $seat) { $request = $baseRequest; $request['legalForms'] = [$legalForm]; $request['legalSeats'] = [$seat]; $requestsToDo[] = $request; } } $fingerprint = hash('sha256', json_encode([ 'email' => $email, 'company' => $companyName, 'deleted' => $baseRequest['deletedFirms'], 'seats' => $seats, 'legalForms' => $legalForms, ], JSON_THROW_ON_ERROR)); try { $taskDirectory = app_task_dir(); $queueLock = fopen(app_private_dir() . DIRECTORY_SEPARATOR . 'queue.lock', 'c+'); if ($queueLock === false || !flock($queueLock, LOCK_EX)) { throw new RuntimeException('Queue lock could not be acquired.'); } try { $taskFiles = glob($taskDirectory . DIRECTORY_SEPARATOR . '*.json') ?: []; $maximumQueueSize = app_env_int('MAX_QUEUE_SIZE', 20, 1, 1000); if (count($taskFiles) >= $maximumQueueSize) { renderPage('Warteschlange voll', 'Momentan werden bereits viele Exporte verarbeitet. Bitte versuchen Sie es später erneut.', true, 503); exit; } foreach ($taskFiles as $existingTaskFile) { $existing = json_decode((string)file_get_contents($existingTaskFile), true); if (is_array($existing) && hash_equals((string)($existing['fingerprint'] ?? ''), $fingerprint)) { renderPage( 'Auftrag bereits vorhanden', 'Ein identischer Auftrag wartet bereits auf die Verarbeitung. Sie erhalten den Download-Link per E-Mail.', false ); exit; } } $taskData = [ 'version' => 2, 'createdAt' => time(), 'requests' => $requestsToDo, 'email' => $email, 'fingerprint' => $fingerprint, 'downloadToken' => bin2hex(random_bytes(16)), ]; $taskFilename = $taskDirectory . DIRECTORY_SEPARATOR . time() . '-' . bin2hex(random_bytes(16)) . '.json'; $temporaryFilename = $taskFilename . '.tmp'; $written = file_put_contents($temporaryFilename, json_encode($taskData, JSON_THROW_ON_ERROR), LOCK_EX); if ($written === false || !rename($temporaryFilename, $taskFilename)) { @unlink($temporaryFilename); throw new RuntimeException('Task file could not be written.'); } @chmod($taskFilename, 0600); } finally { flock($queueLock, LOCK_UN); fclose($queueLock); } renderPage( 'Auftrag eingegangen', 'Wir haben Ihren Auftrag erhalten. Sie erhalten von uns eine E-Mail, sobald die Daten zum Download bereit sind. Dies kann je nach Datenmenge länger dauern.', false ); } catch (Throwable $exception) { error_log('Task creation failed: ' . $exception->getMessage()); renderPage('Fehler', 'Der Auftrag konnte momentan nicht gespeichert werden. Bitte versuchen Sie es später erneut.', true, 503); }