$ok, 'error' => $error ?: null]); } else { header('Location: ' . url('mitglied-werden') . ($ok ? '?sent=1' : '?error=' . $error) . '#anmeldung-fussball', true, 303); } exit; }; $field = static fn (string $key): string => trim((string) ($_POST[$key] ?? '')); // --- Spam-Checks: Bots bekommen ein stilles "OK" (kein Feedback-Kanal) --- if ($field('website') !== '' || !form_token_valid($field('ft'))) { $respond(true); } // --- Eingaben --- $firstName = $field('first_name'); $lastName = $field('last_name'); $birthDate = $field('birth_date'); $phone = $field('phone'); $street = $field('street'); $zip = $field('zip'); $city = $field('city'); $email = $field('email'); $feeClass = $field('fee_class'); // Beitragsklassen-Whitelist identisch zur Seite aus data/mitglied-werden.json ableiten. $data = json_load('mitglied-werden'); $feeOptions = array_map( static fn (array $r): string => $r['label'] . ' — ' . $r['price'], $data['fussball']['fees']['rows'] ?? [] ); // --- Validierung (Whitelist-Prinzip) --- $valid = $firstName !== '' && mb_strlen($firstName) <= 100 && $lastName !== '' && mb_strlen($lastName) <= 100 && preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthDate) === 1 && $phone !== '' && mb_strlen($phone) <= 50 && $street !== '' && mb_strlen($street) <= 200 && $zip !== '' && mb_strlen($zip) <= 10 && $city !== '' && mb_strlen($city) <= 100 && filter_var($email, FILTER_VALIDATE_EMAIL) !== false && in_array($feeClass, $feeOptions, true) && ($_POST['statute'] ?? '') === '1' && ($_POST['privacy'] ?? '') === '1' && !preg_match('/[\r\n]/', $firstName . $lastName . $city . $street); if (!$valid) { $respond(false, 'validation'); } // --- Versand --- $smtp = config('smtp'); $body = "Neuer Mitgliedsantrag (Fußball) über tsv08kulmbach.de\n" . str_repeat('-', 40) . "\n" . "Vorname: {$firstName}\n" . "Nachname: {$lastName}\n" . "Geboren am: {$birthDate}\n" . "Adresse: {$street}, {$zip} {$city}\n" . "Telefon: {$phone}\n" . "E-Mail: {$email}\n" . "Beitragsklasse: {$feeClass}\n" . str_repeat('-', 40) . "\n" . "Satzung anerkannt: ja\n" . "Datenschutz: ja\n" . 'Eingegangen: ' . date('d.m.Y H:i') . "\n\n" . "Hinweis: Bankdaten werden online nicht erhoben – Beitrittserklärung\n" . "per Post zur Unterschrift zusenden.\n"; $buildMailer = static function () use ($smtp): PHPMailer { $mail = new PHPMailer(true); $mail->isSMTP(); $mail->Host = $smtp['host']; $mail->Port = (int) $smtp['port']; $mail->SMTPAuth = true; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Username = $smtp['username']; $mail->Password = $smtp['password']; $mail->CharSet = PHPMailer::CHARSET_UTF8; return $mail; }; // Mail 1 (intern) — erfolgskritisch. try { $mail = $buildMailer(); $mail->setFrom($smtp['from'], $smtp['from_name']); $mail->addAddress($smtp['to']); $mail->addReplyTo($email, $firstName . ' ' . $lastName); $mail->Subject = 'Mitgliedsantrag Fußball: ' . $firstName . ' ' . $lastName; $mail->Body = $body; $mail->send(); } catch (Throwable $e) { error_log('[' . date('c') . '] Mitgliedsantrag-Versand fehlgeschlagen: ' . $e->getMessage() . "\n", 3, STORAGE_PATH . '/logs/mail.log'); $respond(false, 'mail'); } // Mail 2 (Eingangsbestätigung an Antragsteller) — optional, Fehler nicht an Besucher leaken. $welcome = $data['welcome_mail'] ?? null; if (is_array($welcome) && !empty($welcome['subject']) && !empty($welcome['body'])) { try { $confirm = $buildMailer(); $confirm->setFrom($smtp['from'], $smtp['from_name']); $confirm->addAddress($email, $firstName . ' ' . $lastName); $confirm->Subject = $welcome['subject']; $confirm->Body = $welcome['body']; $confirm->send(); } catch (Throwable $e) { error_log('[' . date('c') . '] Bestätigungsmail fehlgeschlagen: ' . $e->getMessage() . "\n", 3, STORAGE_PATH . '/logs/mail.log'); } } $respond(true);