Raiju/api/contact.php
2026-10-25 06:05:42 +03:00

288 lines
9.6 KiB
PHP

<?php
/**
* Contact Form Handler
* Processes form submissions and sends emails via SMTP or PHP mail()
* Also saves all inquiries locally as backup
*/
header('Content-Type: application/json; charset=utf-8');
// Only accept POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit;
}
require_once __DIR__ . '/SmtpMailer.php';
require_once __DIR__ . '/../includes/form-protection.php';
// Load email configuration from settings.json or fallback to smtp-config.php
$emailConfig = null;
$settingsPath = __DIR__ . '/../content/settings.json';
if (file_exists($settingsPath)) {
$settings = json_decode(file_get_contents($settingsPath), true);
if (!empty($settings['email'])) {
$emailConfig = $settings['email'];
}
}
// Fallback: legacy smtp-config.php (for existing installations)
if (!$emailConfig && file_exists(__DIR__ . '/smtp-config.php')) {
require_once __DIR__ . '/smtp-config.php';
$emailConfig = [
'method' => 'smtp',
'recipientEmail' => defined('SMTP_FROM_EMAIL') ? SMTP_FROM_EMAIL : '',
'fromEmail' => defined('SMTP_FROM_EMAIL') ? SMTP_FROM_EMAIL : '',
'fromName' => defined('SMTP_FROM_NAME') ? SMTP_FROM_NAME : '',
'smtpHost' => defined('SMTP_HOST') ? SMTP_HOST : '',
'smtpPort' => defined('SMTP_PORT') ? SMTP_PORT : 587,
'smtpUsername' => defined('SMTP_USERNAME') ? SMTP_USERNAME : '',
'smtpPassword' => defined('SMTP_PASSWORD') ? SMTP_PASSWORD : '',
'smtpEncryption' => defined('SMTP_ENCRYPTION') ? SMTP_ENCRYPTION : 'tls',
];
}
// Path to mail backup file
$mailsFile = __DIR__ . '/../content/mails.json';
/**
* Remove newlines from string (header injection protection)
*/
function sanitizeHeaderValue($value) {
return str_replace(["\r", "\n", "\t"], '', trim($value));
}
function parseConfiguredEmailList($value): array {
$rawItems = is_array($value) ? $value : explode(',', (string)$value);
$emails = [];
foreach ($rawItems as $item) {
foreach (explode(',', (string)$item) as $email) {
$email = sanitizeHeaderValue($email);
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$emails[] = $email;
}
}
}
return array_values(array_unique($emails));
}
/**
* Save email locally as backup
*/
function saveMailBackup($filePath, $mailData) {
return raijuJsonUpdate($filePath, function (array &$mails) use ($mailData): void {
array_unshift($mails, $mailData);
$mails = array_slice($mails, 0, 500);
});
}
// Get and sanitize form data
$lang = $_POST['lang'] ?? 'de';
$name = sanitizeHeaderValue($_POST['name'] ?? '');
$email = sanitizeHeaderValue($_POST['email'] ?? '');
$phone = sanitizeHeaderValue($_POST['phone'] ?? '');
$occasion = sanitizeHeaderValue($_POST['occasion'] ?? '');
$date = sanitizeHeaderValue($_POST['date'] ?? '');
$message = trim($_POST['message'] ?? '');
$clientKey = raijuFormClientKey();
if (!raijuFormReserveRequest($clientKey)) {
http_response_code(429);
echo json_encode([
'success' => false,
'message' => $lang === 'de'
? 'Zu viele Versuche. Bitte versuchen Sie es später erneut.'
: 'Too many attempts. Please try again later.'
]);
exit;
}
// Honeypot check. Return a neutral success response so simple bots cannot
// distinguish this from a real submission, but do not store or send anything.
if (!empty($_POST['website'])) {
raijuFormRecordAttempt($clientKey, false);
echo json_encode([
'success' => true,
'message' => $lang === 'de'
? 'Vielen Dank für Ihre Nachricht!'
: 'Thank you for your message!'
]);
exit;
}
// Validation
$errors = [];
if (empty($name)) {
$errors[] = $lang === 'de' ? 'Name ist erforderlich' : 'Name is required';
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = $lang === 'de' ? 'Gültige E-Mail ist erforderlich' : 'Valid email is required';
}
if (empty($occasion)) {
$errors[] = $lang === 'de' ? 'Anlass ist erforderlich' : 'Occasion is required';
}
if (empty($message)) {
$errors[] = $lang === 'de' ? 'Nachricht ist erforderlich' : 'Message is required';
}
if (!empty($errors)) {
echo json_encode(['success' => false, 'message' => implode(', ', $errors)]);
exit;
}
if (raijuFormLooksLikeSpam([$name, $email, $phone, $occasion, $date, $message])) {
raijuFormRecordAttempt($clientKey, false);
echo json_encode([
'success' => false,
'message' => $lang === 'de'
? 'Die Nachricht konnte nicht gesendet werden. Bitte prüfen Sie Ihre Eingaben.'
: 'The message could not be sent. Please check your input.'
]);
exit;
}
$tokenResult = raijuValidateFormToken($_POST['form_token'] ?? null, 'contact');
if (empty($tokenResult['valid'])) {
raijuFormRecordAttempt($clientKey, false);
$newToken = raijuCreateFormToken('contact');
http_response_code(400);
echo json_encode([
'success' => false,
'formToken' => $newToken,
'message' => $lang === 'de'
? 'Das Formular ist abgelaufen oder wurde zu schnell gesendet. Bitte versuchen Sie es erneut.'
: 'The form expired or was submitted too quickly. Please try again.'
]);
exit;
}
// Compose email
$subject = ($lang === 'de' ? 'Anfrage über Website: ' : 'Website inquiry: ') . $occasion;
$body = $lang === 'de'
? "Neue Anfrage über das Kontaktformular\n"
: "New inquiry via contact form\n";
$body .= str_repeat('-', 40) . "\n\n";
$body .= ($lang === 'de' ? "Name: " : "Name: ") . $name . "\n";
$body .= ($lang === 'de' ? "E-Mail: " : "Email: ") . $email . "\n";
if ($phone) {
$body .= ($lang === 'de' ? "Telefon: " : "Phone: ") . $phone . "\n";
}
$body .= ($lang === 'de' ? "Anlass: " : "Occasion: ") . $occasion . "\n";
if ($date) {
$formattedDate = date('d.m.Y', strtotime($date));
$body .= ($lang === 'de' ? "Gewünschtes Datum: " : "Preferred date: ") . $formattedDate . "\n";
}
$body .= "\n" . ($lang === 'de' ? "Nachricht:\n" : "Message:\n");
$body .= str_repeat('-', 40) . "\n";
$body .= $message . "\n";
$body .= str_repeat('-', 40) . "\n";
// Send email when configured. Valid submissions are stored locally even when
// email delivery is disabled or misconfigured, so contact requests are not lost.
$sent = false;
$method = $emailConfig['method'] ?? 'inactive';
$toRecipients = parseConfiguredEmailList($emailConfig['recipientEmail'] ?? '');
$bccRecipients = parseConfiguredEmailList($emailConfig['bccEmail'] ?? '');
$to = implode(', ', $toRecipients);
$fromEmail = sanitizeHeaderValue($emailConfig['fromEmail'] ?? '') ?: ($toRecipients[0] ?? '');
$fromName = sanitizeHeaderValue($emailConfig['fromName'] ?? '');
$replyToEmail = filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : '';
$isEmailConfigured = $emailConfig
&& $method !== 'inactive'
&& !empty($toRecipients);
if ($isEmailConfigured && $method === 'smtp') {
$mailer = new SmtpMailer(
$emailConfig['smtpHost'] ?? '',
intval($emailConfig['smtpPort'] ?? 587),
$emailConfig['smtpUsername'] ?? '',
$emailConfig['smtpPassword'] ?? '',
$emailConfig['smtpEncryption'] ?? 'tls'
);
$sent = $mailer->send(
$toRecipients,
$subject,
$body,
$fromEmail,
$fromName,
$replyToEmail,
$bccRecipients
);
} elseif ($isEmailConfigured && $method === 'sendmail') {
// PHP mail() — requires server IP to be whitelisted for sending
$headers = [];
$headers[] = 'From: ' . ($fromName ? "=?UTF-8?B?" . base64_encode($fromName) . "?= <$fromEmail>" : $fromEmail);
if ($replyToEmail) {
$headers[] = 'Reply-To: ' . $replyToEmail;
}
$headers[] = 'Content-Type: text/plain; charset=UTF-8';
$headers[] = 'X-Mailer: Raiju CMS';
$sent = @mail($to, '=?UTF-8?B?' . base64_encode($subject) . '?=', $body, implode("\r\n", $headers));
foreach ($bccRecipients as $bccRecipient) {
$sent = @mail($bccRecipient, '=?UTF-8?B?' . base64_encode($subject) . '?=', $body, implode("\r\n", $headers)) && $sent;
}
}
$status = $sent ? 'sent' : ($isEmailConfigured ? 'send_failed' : 'local_only');
$saved = saveMailBackup($mailsFile, [
'id' => uniqid('mail_'),
'timestamp' => date('c'),
'lang' => $lang,
'formId' => 'contact',
'formLabel' => $lang === 'de' ? 'Kontaktformular' : 'Contact form',
'name' => $name,
'email' => $email,
'phone' => $phone,
'occasion' => $occasion,
'date' => $date,
'message' => $message,
'fields' => [
['key' => 'name', 'label' => $lang === 'de' ? 'Name' : 'Name', 'value' => $name],
['key' => 'email', 'label' => $lang === 'de' ? 'E-Mail' : 'Email', 'value' => $email],
['key' => 'phone', 'label' => $lang === 'de' ? 'Telefon' : 'Phone', 'value' => $phone],
['key' => 'message', 'label' => $lang === 'de' ? 'Nachricht' : 'Message', 'value' => $message],
],
'status' => $status,
'read' => false,
'starred' => false
]);
if (!$saved) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => $lang === 'de'
? 'Die Nachricht konnte nicht gespeichert werden. Bitte versuchen Sie es später erneut.'
: 'The message could not be saved. Please try again later.'
]);
exit;
}
raijuFormRecordAttempt($clientKey, true);
$newToken = raijuCreateFormToken('contact');
echo json_encode([
'success' => true,
'status' => $status,
'formToken' => $newToken,
'message' => $lang === 'de'
? 'Vielen Dank für Ihre Nachricht!'
: 'Thank you for your message!'
]);