Files

146 lines
5.6 KiB
PHP

<?php
// Session pour CSRF et rate-limit
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$contactEmail = $_ENV['CONTACT_EMAIL'] ?? '';
$error = null;
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// CSRF
$token = $_POST['_token'] ?? '';
if (!hash_equals($_SESSION['contact_csrf'] ?? '', $token)) {
$error = 'Requête invalide. Veuillez réessayer.';
}
// Honeypot (champ caché que les bots remplissent)
if (!$error && ($_POST['_hp'] ?? '') !== '') {
$error = 'Requête invalide.';
}
// Rate-limit : 1 message par 5 minutes par session
if (!$error) {
$lastSent = $_SESSION['contact_last_sent'] ?? 0;
if (time() - $lastSent < 300) {
$error = 'Merci d\'attendre quelques minutes avant d\'envoyer un nouveau message.';
}
}
// Validation des champs
$name = trim($_POST['name'] ?? '');
$from = trim($_POST['email'] ?? '');
$subject = trim($_POST['subject'] ?? 'Contact depuis varlog');
$body = trim($_POST['message'] ?? '');
if (!$error) {
if ($name === '' || mb_strlen($name) > 100) {
$error = 'Nom invalide.';
} elseif (!filter_var($from, FILTER_VALIDATE_EMAIL)) {
$error = 'Adresse e-mail invalide.';
} elseif ($body === '' || mb_strlen($body) > 5000) {
$error = 'Message vide ou trop long (max 5000 caractères).';
}
}
if (!$error && $contactEmail !== '') {
$subjectClean = mb_encode_mimeheader(
'[' . siteTitle() . ' contact] ' . mb_strimwidth($subject, 0, 100, '…'),
'UTF-8',
'B'
);
$nameClean = mb_encode_mimeheader($name, 'UTF-8', 'B');
$fromEmail = $_ENV['CONTACT_FROM_EMAIL'] ?? ('noreply@' . (parse_url(APP_URL, PHP_URL_HOST) ?? 'localhost'));
$headers = 'From: =?UTF-8?B?' . base64_encode(siteTitle() . ' contact') . "?= <{$fromEmail}>\r\n";
$headers .= "Reply-To: {$nameClean} <{$from}>\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
$headers .= "Content-Transfer-Encoding: 8bit\r\n";
$fullBody = "De : {$name} <{$from}>\n\n{$body}\n\n---\nEnvoyé depuis varlog";
if (@mail($contactEmail, $subjectClean, $fullBody, $headers)) {
$_SESSION['contact_last_sent'] = time();
$success = true;
} else {
$error = 'Erreur lors de l\'envoi. Veuillez réessayer plus tard.';
}
} elseif (!$error) {
$error = 'Formulaire de contact non configuré.';
}
}
// Génère un nouveau token CSRF à chaque affichage du formulaire
if (!$success) {
$_SESSION['contact_csrf'] = bin2hex(random_bytes(16));
}
ob_start();
?>
<div class="posts-list">
<h1 class="mb-1">Contact</h1>
<p class="text-muted mb-4">Envoyez-moi un message. Votre adresse e-mail ne sera pas publiée.</p>
<?php if ($success): ?>
<div class="alert alert-success" role="alert">
Message envoyé. Je vous répondrai dès que possible.
</div>
<?php else: ?>
<?php if ($error): ?>
<div class="alert alert-danger" role="alert"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<div class="card">
<div class="card-body">
<form method="POST" action="/?action=contact" novalidate>
<input type="hidden" name="_token" value="<?= htmlspecialchars($_SESSION['contact_csrf']) ?>">
<!-- Honeypot -->
<div style="display:none" aria-hidden="true">
<input type="text" name="_hp" tabindex="-1" autocomplete="off">
</div>
<div class="mb-3">
<label for="contact-name" class="form-label">Nom <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="contact-name" name="name"
value="<?= htmlspecialchars($_POST['name'] ?? '') ?>"
maxlength="100" required autocomplete="name">
</div>
<div class="mb-3">
<label for="contact-email" class="form-label">Adresse e-mail <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="contact-email" name="email"
value="<?= htmlspecialchars($_POST['email'] ?? '') ?>"
required autocomplete="email">
</div>
<div class="mb-3">
<label for="contact-subject" class="form-label">Sujet</label>
<input type="text" class="form-control" id="contact-subject" name="subject"
value="<?= htmlspecialchars($_POST['subject'] ?? '') ?>"
maxlength="150" autocomplete="off">
</div>
<div class="mb-4">
<label for="contact-message" class="form-label">Message <span class="text-danger">*</span></label>
<textarea class="form-control" id="contact-message" name="message"
rows="7" maxlength="5000" required><?= htmlspecialchars($_POST['message'] ?? '') ?></textarea>
</div>
<button type="submit" class="btn btn-success">Envoyer</button>
</form>
</div>
</div>
<?php endif; ?>
</div>
<?php
$content = ob_get_clean();
$title = 'Contact — ' . siteTitle();
include __DIR__ . '/layout.php';