288 lines
9.6 KiB
PHP
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!'
|
|
]);
|