isSMTP(); $mail->Host = $MAILCFG['smtp']['host']; $mail->Port = (int)$MAILCFG['smtp']['port']; $mail->SMTPSecure = $MAILCFG['smtp']['secure'] ?? PHPMailer::ENCRYPTION_STARTTLS; $mail->SMTPAuth = (bool)$MAILCFG['smtp']['auth']; $mail->Username = $MAILCFG['smtp']['username'] ?? ''; $mail->Password = $MAILCFG['smtp']['password'] ?? ''; // Base $mail->CharSet = 'UTF-8'; $mail->Encoding = 'base64'; $mail->XMailer = ''; // Envelope (Return-Path) if (!empty($MAILCFG['bounce'])) $mail->Sender = $MAILCFG['bounce']; // From / Reply-To $from = $MAILCFG['from'] ?? ['no-reply@uslugi.fr', 'USLUGI.FR']; $rpto = $MAILCFG['reply_to'] ?? ['contact@uslugi.fr', 'USLUGI.FR']; $mail->setFrom($from[0], $from[1] ?? ''); if (!empty($rpto[0])) $mail->addReplyTo($rpto[0], $rpto[1] ?? ''); // Destinataire $mail->addAddress($toEmail, $toName); // Anti auto-replies (optionnel) if (!empty($MAILCFG['suppress_auto'])) { $mail->addCustomHeader('Auto-Submitted', 'auto-generated'); $mail->addCustomHeader('X-Auto-Response-Suppress', 'All'); } // Corps $mail->isHTML(true); $mail->Subject = $subject; $mail->Body = $htmlBody; $mail->AltBody = $textBody; // 📎 Invitation iCalendar $mail->addStringAttachment( $ical, $filename, 'base64', 'text/calendar; method=REQUEST; charset=UTF-8' ); $mail->send(); return true; } catch (Throwable $e) { error_log('PHPMailer RDV send exception: '.$e->getMessage()); if (property_exists($mail, 'ErrorInfo') && $mail->ErrorInfo) { error_log('PHPMailer RDV ErrorInfo: '.$mail->ErrorInfo); } return false; } } /* ================== Valeurs par défaut Online/Offline ================== */ if (!isset($c_online)) { $c_online = 'online'; } if (!isset($c_offline)) { $c_offline = 'offline'; } /* ================== CSRF pour l'appel AJAX ================== */ if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(16)); } $CSRF = $_SESSION['csrf_token']; /* ================== Helpers communs (HTML UI) ================== */ function ui_head(string $title): void { $HEAD_EXTRA = << html,body{height:100%} body{ margin:0; background:#8CA9FF; font-family:'Montserrat',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; display:flex; align-items:center; justify-content:center; padding:32px 16px; } .page-loader{ position:fixed; inset:0; z-index:9999; background:#8CA9FF; display:flex; align-items:center; justify-content:center; transition:opacity .25s ease, visibility .25s ease; } .hidden{opacity:0; visibility:hidden;} .loader-card{ background:#fff; color:#38378B; border-radius:22px; padding:20px 22px; box-shadow:0 12px 40px rgba(0,0,0,.08); display:flex; align-items:center; gap:14px; } .loader-spinner{ width:36px; height:36px; border-radius:50%; border:3px solid rgba(56,55,139,.15); border-top-color:#38378B; animation:spin 1s linear infinite; } @keyframes spin{ to{ transform:rotate(360deg);} } .loader-text{ font-weight:700; } .wrap{ width:min(640px, 100%); margin:0 auto; } .msg{ background:#fff; color:rgb(33,37,41); border-radius:22px; box-shadow:0 12px 40px rgba(0,0,0,.08); padding:22px; text-align:center; } .msg h2{ margin:0 0 10px; font-size:16px; font-weight:400; } .msg p{ margin:10px 0; line-height:1.5; } .msg .meta{ margin:12px auto 0; padding:12px 14px; border:1px solid #f11e1eff; border-radius:16px; background:#F7F8FF; color:#f11e1eff; font-weight:600; display:inline-block; } .msg .actions{ margin-top:16px; display:flex; gap:10px; justify-content:center; flex-wrap:wrap; } .btn{ display:inline-block; text-decoration:none; font-weight:700; border-radius:999px; padding:10px 16px; } .btn-primary{ background:#E03C3F; color:#fff; box-shadow:0 8px 22px rgba(0,0,0,.12); } .btn-primary:hover{ filter:brightness(1.05); } .btn-outline{ background:#fff; color:#38378B; border:1px solid #E0E3EB; } .btn-outline:hover{ background:#f8faff; } .msg--ok h2::before{ content:" "; } .msg--err h2{ color:#666666; } .msg--err h2::before{ content:"❌ "; } HTML; ?>