Files
Claude 82dc9599cc Maßgeschneiderte Website für Olzinger Haustechnik
Neuer statischer Webauftritt für die Olzinger GmbH (Heizung, Sanitär,
Lüftung, regenerative Energien – Ergolding bei Landshut, seit 1985).

- Hero mit live berechnetem WebGL-„Wärmebild“: Maus als Wärmequelle,
  Wärmespur, Isothermen und Temperaturanzeige; Preloader „Thermostat“
- Gestapelte Leistungskarten mit animierten SVG-Illustrationen
- Interaktives Energie-Haus (Wärmepumpe, Solarthermie, Speicher,
  Fußbodenheizung, Bad, Lüftung) mit Tabs, Hotspots und Autoplay
- Heizungs-Finder mit Empfehlung und Übernahme ins Kontaktformular
- Ablauf als sich füllende Rohrleitung, Zeitleiste mit Zählwerk,
  Live-Öffnungsstatus inkl. bayerischer Feiertage
- Kontaktformular mit PHP-Versand (Honeypot, Zeitfalle) und
  mailto-Fallback; Impressum, Datenschutz, 404-Seite
- Smooth-Scroll (Lenis) und GSAP-Animationen, alles lokal gehostet;
  reduzierte Bewegung, Betrieb ohne JavaScript, axe ohne Befunde
- SEO: Schema.org HVACBusiness, Open-Graph-Bild, Sitemap, robots.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kjd7UMh3HKyVziCgiZ9A2i
2026-10-03 16:49:05 +00:00

118 lines
3.6 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Olzinger Haustechnik – Versand des Kontaktformulars
*
* Wird per fetch() aus assets/js/contact.js aufgerufen und antwortet mit JSON.
* Ist PHP auf dem Server nicht verfügbar, öffnet das Formular automatisch das
* E-Mail-Programm des Besuchers (Fallback in contact.js).
*
* Schutzmaßnahmen: Honeypot-Feld, Mindest-Ausfüllzeit, Längenbegrenzung,
* Validierung der E-Mail-Adresse (verhindert Header-Injection über Reply-To),
* kodierter Betreff und feste Empfängeradresse.
*/
declare(strict_types=1);
const RECIPIENT = 'kontakt@olzinger.de';
// Absender sollte eine Adresse der eigenen Domain sein (SPF/DKIM), sonst landen Mails im Spam.
const SENDER = 'kontakt@olzinger.de';
const MIN_SECONDS = 3;
date_default_timezone_set('Europe/Berlin');
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('Cache-Control: no-store');
function respond(int $status, array $payload): void
{
http_response_code($status);
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
exit;
}
function field(string $key, int $max): string
{
$value = isset($_POST[$key]) && is_string($_POST[$key]) ? trim($_POST[$key]) : '';
$value = str_replace(["\r\n", "\r"], "\n", $value);
// Steuerzeichen außer Zeilenumbruch und Tab entfernen
$value = preg_replace('/[^\P{C}\n\t]/u', '', $value) ?? '';
return function_exists('mb_substr') ? mb_substr($value, 0, $max, 'UTF-8') : substr($value, 0, $max);
}
function single_line(string $value): string
{
return trim(preg_replace('/\s+/u', ' ', $value) ?? '');
}
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
header('Allow: POST');
respond(405, ['ok' => false, 'error' => 'method_not_allowed']);
}
// Honeypot: echte Besucher sehen dieses Feld nicht.
if (field('website', 200) !== '') {
respond(200, ['ok' => true]);
}
// Zeitfalle: Bots senden Formulare meist sofort ab.
$started = (int) ($_POST['ts'] ?? 0);
if ($started > 0 && (microtime(true) * 1000 - $started) < MIN_SECONDS * 1000) {
respond(200, ['ok' => true]);
}
$name = single_line(field('name', 120));
$email = single_line(field('email', 200));
$phone = single_line(field('phone', 60));
$place = single_line(field('place', 120));
$topic = single_line(field('topic', 120));
$message = field('message', 5000);
$privacy = !empty($_POST['privacy']);
$errors = [];
if ($name === '') {
$errors[] = 'name';
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors[] = 'email';
}
if ((function_exists('mb_strlen') ? mb_strlen($message, 'UTF-8') : strlen($message)) < 5) {
$errors[] = 'message';
}
if (!$privacy) {
$errors[] = 'privacy';
}
if ($errors) {
respond(422, ['ok' => false, 'errors' => $errors]);
}
$subject = 'Anfrage über olzinger.de' . ($topic !== '' ? ' – ' . $topic : '');
$body = implode("\n", [
'Neue Anfrage über das Kontaktformular auf olzinger.de',
'',
'Anliegen: ' . ($topic !== '' ? $topic : '–'),
'Name: ' . $name,
'E-Mail: ' . $email,
'Telefon: ' . ($phone !== '' ? $phone : '–'),
'PLZ / Ort: ' . ($place !== '' ? $place : '–'),
'',
'Nachricht:',
$message,
'',
'—',
'Gesendet am ' . date('d.m.Y') . ' um ' . date('H:i') . ' Uhr',
]);
$headers = implode("\r\n", [
'From: Website Olzinger <' . SENDER . '>',
'Reply-To: ' . $email,
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=UTF-8',
'Content-Transfer-Encoding: 8bit',
'X-Mailer: olzinger.de',
]);
$encodedSubject = '=?UTF-8?B?' . base64_encode($subject) . '?=';
$sent = @mail(RECIPIENT, $encodedSubject, $body, $headers, '-f' . SENDER);
respond($sent ? 200 : 500, ['ok' => (bool) $sent]);